Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2006.08.27;
Скачать: CL | DM;

Вниз

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

 
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;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.051 c
2-1155087755
vladimirg88
2006-08-09 05:42
2006.08.27
Форма


2-1154609678
webpauk
2006-08-03 16:54
2006.08.27
TreeView select item


1-1152863112
oleggar
2006-07-14 11:45
2006.08.27
decimalseparator


15-1151261024
VirEx
2006-06-25 22:43
2006.08.27
JDMClient


15-1154372410
Gydvin
2006-07-31 23:00
2006.08.27
Зацените компонент