Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2006.08.27;
Скачать: [xml.tar.bz2];

Вниз

Правильная синхронизация   Найти похожие ветки 

 
SamProf ©   (2006-08-08 13:37) [0]

Мастера, прошу помоч с такой проблемой:
Есть файл этак 70-150 метров
Есть много приложений(потоков)-читателей (они этот файл читают, обрабатывают и выдают резулбтат юзеру)
Есть одно приложение - писатель (он полностью переписывает файл)
Проблема: Сами по себе читатели могут в одновременно читать этот файл
Как тока писатель собрался писать в файл - он должен дождатся завершения уже начавших чтение потоков и не пускать вновь созданых читателей, потом писать в файл (читатели ждут), как тока писатель закончит - разрешить чтение всем читателям...

Как это сделать по уму?


 
MBo ©   (2006-08-08 13:42) [1]

TMultiReadExclusiveWriteSynchronizer


 
clickmaker ©   (2006-08-08 13:44) [2]


> Как тока писатель собрался писать в файл - он должен дождатся
> завершения уже начавших чтение потоков

читатель создает Mutex на время чтения, писатель WaitForMultipleObjects их
потом монопольно открывает файл (0 в sharemode ф-ии CreateFile), либо сам создает mutex, а клиенты WaitForSingleObject его
Насчет не пускать новых - можно семафор заюзать


 
SamProf ©   (2006-08-08 13:55) [3]

>MBo ©   (08.08.06 13:42) [1]
>TMultiReadExclusiveWriteSynchronizer

А он работает в рамках одного приложения?


 
Leonid Troyanovsky ©   (2006-08-08 14:00) [4]


> MBo ©   (08.08.06 13:42) [1]

> TMultiReadExclusiveWriteSynchronizer


Он не IPC. Лучше, тут что-то типарихтеровского  SWMRG

http://www.codeguru.com/forum/showthread.php?s=&threadid=119395

http://groups.google.com/group/fido7.ru.delphi.info/msg/2cdccacbf3938fe2

--
Regards, LVT.


 
Сергей М. ©   (2006-08-08 14:13) [5]


> SamProf ©   (08.08.06 13:37)


"Писатель" может поступать так (псевдокод):

TmpFile = CopyFile(SharedFile); //делается врем.копия совместно используемого файла
Prepare(TmpFile); //содержимое копии обновляется
while not MoveFile(TmpFile -> SharedFile) do sleep; //как только "читатели" освободят ориг.файл, на его место записывается обновленный врем.файл


 
DiamondShark ©   (2006-08-08 14:27) [6]

Нехороший псевдокод.
Race condition типичнейший. ;)
При достаточно резвой активности читателей писатель может дожидаться освобождения файла до второго пришествия.

Если совсем по-уму, то читателей надо вообще изолировать от файла.
Пусть пользуются прикладным интерфейсом писателя.


 
Сергей М. ©   (2006-08-08 14:44) [7]


> DiamondShark ©   (08.08.06 14:27) [6]


> При достаточно резвой активности читателей писатель может
> дожидаться освобождения файла до второго пришествия


Равно, заметь, как и сигнала объекта синхронизации - до того же самого пришествия)


> Если совсем по-уму


Если совсем уж по уму, то арбитражем доступа к ресурсу (со стороны и писателей и читателей) должен заведовать специально реализованный сервис.


 
Сергей М. ©   (2006-08-08 14:46) [8]


> DiamondShark ©   (08.08.06 14:27) [6]


Не показатель, конечно, но мелкомягкие в своих офисных "шедеврах" именно так и поступают)


 
DiamondShark ©   (2006-08-08 14:57) [9]


> Равно, заметь, как и сигнала объекта синхронизации - до
> того же самого пришествия)

а вот и нет.

разница между

SyncObj.Acqure(INFINITE)

и

while not SyncObj.Acqure(0) do Sleep;

есть. И существенная.

В первом случае время ожидания -- функция от интенсивности запросов, во втором -- от погоды на Марсе.
У меня по неопытности как-то случилось, что при двух (!) конкурентах один дожидался обслуживания десятки минут (при длительности рабочих циклов порядка сотен миллисекунд). Разумеется, ситуация не воспроизводилась на другой машине :)


> Если совсем уж по уму, то арбитражем доступа к ресурсу (со
> стороны и писателей и читателей) должен заведовать специально
> реализованный сервис

Это и имел в виду ;)


 
DiamondShark ©   (2006-08-08 15:01) [10]


> В первом случае время ожидания -- функция от интенсивности
> запросов

Даже не интенсивности, а всего лишь длительности одного запроса.
МО = 1\2 рабочего цикла
:)


 
Сергей М. ©   (2006-08-08 15:02) [11]


> DiamondShark ©   (08.08.06 14:57) [9]


И в том и в другом случае ситуация со вторым пришествием не исключена.


 
clickmaker ©   (2006-08-08 15:07) [12]

конечно, если читатель настолько зачитается, что забудет обо всем... Не от хорошей же жизни в СУБД всякие таймауты наворачивают...


 
SamProf ©   (2006-08-08 15:26) [13]

У меня вот такое вот решение. Вопрос - могут ли бытьтраблы?
Если читатель ваще не получит доступ у файлу пока писатель пишет - это не стрпшно!

Вот код читателя:
try
count:=0;
    if not FileExists(fname) then exit;
    AssignFile(f,fname);
    Reset(f,1);
    RecRead(false);
    closefile(f);
    except
    end;


А вот писателю написать обязательно:
bool := false;
    while not bool do
         begin
         bool:=true;
         try
              AssignFile(f,fname);
              rewrite(f,1);
              items.Savetofile(f);
              closefile(f);
         except
              bool:=false;
              end;
         end;


 
clickmaker ©   (2006-08-08 15:29) [14]


> Вопрос - могут ли бытьтраблы?

еще какие!
самая маленькая - 100% загрузка проца


 
SamProf ©   (2006-08-08 15:30) [15]

А кроме этого?


 
clickmaker ©   (2006-08-08 15:37) [16]


> SamProf ©   (08.08.06 15:30) [15]

то есть тебе мало?
рекомендую все-таки прислушаться к вышеизложенным советам.
потому что писать циклы while (true) для достукивания к файлам - не есть гуд. Это не ДОС, это многозадачная система


 
SamProf ©   (2006-08-08 15:38) [17]

Больше всего меня интересует целостность файла! Он не испоганится?


 
tesseract ©   (2006-08-08 16:19) [18]


> clickmaker ©   (08.08.06 15:29) [14]


согласен, открытие файла весьма ёмкая задачка.

А не проще ли накатать отдельный  поток для работы с файлом и  организовать там потокобезопасную очередь ?


 
Пусик ©   (2006-08-08 19:23) [19]

Вобщем-то, задача так себе...

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, SyncObjs, StdCtrls;

type

 THWriter=class(TThread)
 private
   FMsg: String;
   procedure Sync;
 protected
   procedure Execute; override;
 public
   FThreadCounter: Integer;
   FEventWriter: TEvent;
   FEventReader: TEvent;
   constructor Create;
   destructor Destroy; override;
 end;

 THReader=class(TThread)
 private
   FPThreadCounter: PInteger;
   FEventWriter: TEvent;
   FEventReader: TEvent;
   procedure Sync;
 protected
   procedure Execute; override;
 public
   constructor Create(Writer,Reader: TEvent; PCounter: PInteger);
 end;

 TForm1 = class(TForm)
   Button1: TButton;
   Label1: TLabel;
   Label2: TLabel;
   procedure Button1Click(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
 end;

var
 Form1: TForm1;

implementation

{$R *.dfm}

{ THWriter }

constructor THWriter.Create;
begin
 inherited Create(True);
 FreeOnTerminate := True;
 FEventWriter := TEvent.Create(nil,True,False,"");
 FEventreader := TEvent.Create(nil,True,True,"");
 FThreadCounter := 0;
 with TFileStream.Create("testfile",fmCreate) do Free;
end;

destructor THWriter.Destroy;
begin
 FEventWriter.Free;
 FEventReader.Free;
end;

procedure THWriter.Execute;
var
 fs: TFileStream;
 Buf: String;
 i: Integer;
begin
 SetLength(Buf,1024);
 while not Terminated do
 begin
   FEventWriter.ResetEvent;
   FEventReader.WaitFor(INFINITE);
   FMsg := "Writer: Работаю...";
   Synchronize(Sync);
   try
     fs := TFileStream.Create("testfile",fmOpenReadWrite);
     try
       for i := 1 to 10 do
       begin
         fs.Write(Buf[1],1024);
         Sleep(100);
       end;
     finally
       fs.Free;
       FEventReader.ResetEvent;
       FEventWriter.SetEvent;
     end;
   except
     MessageBox(0,"Error Writer","Error Writer",MB_OK);
   end;
   FMsg := "Writer: Жду...";
   Synchronize(Sync);

//    Sleep(3000);
 end;
end;

procedure THWriter.Sync;
begin
 Form1.Label1.Caption := FMsg;
end;

{ THReader }

constructor THReader.Create(Writer, Reader: TEvent; PCounter: PInteger);
begin
 inherited Create(True);
 FreeOnTerminate := True;
 FPThreadCounter := PCounter;
 FEventWriter := Writer;
 FEventReader := Reader;
 Resume;
end;

procedure THReader.Execute;
var
 fs: TFileStream;
 Buf: String;
begin
 SetLength(Buf,1024);
 while not Terminated do
 begin
   Sleep(10);
   Synchronize(Sync);
   FEventWriter.WaitFor(INFINITE);
   InterlockedIncrement(FPThreadCounter^);
   FEventReader.ResetEvent;
   Synchronize(Sync);
   try
     Sleep(Random(1000));
     fs := TFileStream.Create("testfile", fmOpenRead or fmShareDenyWrite);
     try
       fs.Read(Buf[1],1024);
     finally
       fs.Free;
       if InterlockedDecrement(FPThreadCounter^)=0 then FEventReader.SetEvent;
     end;
   except
     MessageBox(0,"Error Reader","Error Reader",MB_OK);
   end;

 end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 i: Integer;
begin
 with THWriter.Create do
 begin
   for i := 0 to 50 do THreader.Create(FEventWriter, FEventReader,@FThreadCounter);
   Resume;
 end;
end;

procedure THReader.Sync;
begin
 Form1.Label2.Caption := "Читателей: "+IntToStr(FPThreadCounter^);
end;

end.


 
DiamondShark ©   (2006-08-08 19:43) [20]

Хитренькие какие.
Автору между процессами надо синхронизироваться.


 
Пусик ©   (2006-08-08 19:47) [21]

А-а... Прошу прощения. Почему-то в голове отложилось, что внутри одного процесса.


 
Пусик ©   (2006-08-08 19:49) [22]

В принципе, межпроцессорная синхронизация сложнее только в реализации, логически ничего сложного.


 
Пусик ©   (2006-08-08 19:52) [23]


> межпроцессорная

межпроцессная.


 
Джо ©   (2006-08-08 19:55) [24]

> [23] Пусик ©   (08.08.06 19:52)
>
> > межпроцессорная
>
> межпроцессная.

Ну да, межпроцессорная это несколько сложнее :)


 
DiamondShark ©   (2006-08-08 23:03) [25]

Может, кому-нибудь понадобится.
Интерпроцессная версия MultiReadExclusiveWriteSynchronizer.


unit imrews;

interface

uses
 Windows;

type
 TInterprocessMultiReadExclusiveWriteSynchronizer = class
 private
   FWriteMutex: THandle;
   FNoReadersEvent: THandle;
   FMapping: THandle;
   FReaderCountPtr: PInteger;
 public
   constructor Create(const Name: String);
   destructor Destroy; override;
   procedure BeginRead;
   procedure BeginWrite;
   procedure EndRead;
   procedure EndWrite;
 end;

implementation

uses
 SysUtils;

{ TInterprocessMultiReadExclusiveWriteSynchronizer }

procedure TInterprocessMultiReadExclusiveWriteSynchronizer.BeginRead;
begin
 Win32Check(WaitForSingleObject(FWriteMutex, INFINITE) <> WAIT_FAILED);
 try
   Inc(FReaderCountPtr^);
   ResetEvent(FNoReadersEvent);
 finally
   ReleaseMutex(FWriteMutex);
 end
end;

procedure TInterprocessMultiReadExclusiveWriteSynchronizer.BeginWrite;
var
 Handles: array[0..1] of THandle;
begin
 Handles[0] := FWriteMutex; Handles[1] := FNoReadersEvent;
 Win32Check(
   WaitForMultipleObjects(2, @Handles, True, INFINITE) <> WAIT_FAILED);
end;

constructor TInterprocessMultiReadExclusiveWriteSynchronizer.Create(
 const Name: String);
var
 MutexName,
 EventName,
 MappingName: String;
 IamFirst: Boolean;
begin
 MutexName := Name + "_Mutex";
 EventName := Name + "_Event";
 MappingName := Name + "_Mapping";
 FWriteMutex := CreateMutex(nil, True, PChar(MutexName));
 Win32Check(FWriteMutex <> 0);
 IamFirst := GetLastError = 0;
 FNoReadersEvent := CreateEvent(nil, True, True, PChar(EventName));
 Win32Check(FNoReadersEvent <> 0);
 FMapping := CreateFileMapping(
   INVALID_HANDLE_VALUE,
   nil,
   PAGE_READWRITE,
   0,
   SizeOf(Integer),
   PChar(MappingName));
 Win32Check(FMapping <> 0);
 FReaderCountPtr := MapViewOfFile(
   FMapping,
   FILE_MAP_WRITE,
   0,
   0,
   SizeOf(Integer));
 Win32Check(FReaderCountPtr <> nil);
 if IamFirst then
   begin
     FReaderCountPtr^ := 0;
     ReleaseMutex(FWriteMutex);
   end;
end;

destructor TInterprocessMultiReadExclusiveWriteSynchronizer.Destroy;
begin
 if FReaderCountPtr <> nil then UnmapViewOfFile(FReaderCountPtr);
 if FMapping <> 0 then CloseHandle(FMapping);
 if FNoReadersEvent <> 0 then CloseHandle(FNoReadersEvent);
 if FWriteMutex <> 0 then CloseHandle(FWriteMutex);
 inherited;
end;

procedure TInterprocessMultiReadExclusiveWriteSynchronizer.EndRead;
begin
 Win32Check(WaitForSingleObject(FWriteMutex, INFINITE) <> WAIT_FAILED);
 try
   Dec(FReaderCountPtr^);
   if FReaderCountPtr^ = 0 then SetEvent(FNoReadersEvent);
 finally
   ReleaseMutex(FWriteMutex);
 end
end;

procedure TInterprocessMultiReadExclusiveWriteSynchronizer.EndWrite;
begin
 Win32Check(ReleaseMutex(FWriteMutex));
end;

end.


 
redlord   (2006-08-08 23:06) [26]

не плохо былобы узнать что за инфа лежит в файле а то может перекинуть все на плечи баз данных .  пусть они думает ))
я бы зделал когонить главным либо читало либо писало и пусть он заведует доступом и раздает привелегии а если надо то и отрубает конкурентов


 
Leonid Troyanovsky ©   (2006-08-09 10:35) [27]


> DiamondShark ©   (08.08.06 23:03) [25]


Если вместо mmf использовать семафор, то получится классический
SWMRG ([4]). Одна из присущих ему проблем в том, что когда
читателей много, то они могут полностью блокировать работу
писателя. Отсюда, например, необходимость в hEventUnlockBeginRead.

Но, в любом случае, для реальных приложений возможностей
подобного объекта явно не хватает.

--
Regards, LVT.



Страницы: 1 вся ветка

Форум: "Начинающим";
Текущий архив: 2006.08.27;
Скачать: [xml.tar.bz2];

Наверх




Память: 0.54 MB
Время: 0.083 c
15-1154515042
Kerk
2006-08-02 14:37
2006.08.27
Что такое "харизма"?


1-1152863944
vigo_
2006-07-14 11:59
2006.08.27
Выделение цветом строк в TDBGrid.


2-1154628757
Exit
2006-08-03 22:12
2006.08.27
Сеть


15-1154118328
QuickFinder
2006-07-29 00:25
2006.08.27
Commercial Open Source


1-1153111816
Степан
2006-07-17 08:50
2006.08.27
Цвет фона в ListView





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский