![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Правильная синхронизация [D7, WinXP]
SamProf © (2006-08-08 13:37:00)Мастера, прошу помоч с такой проблемой:
Есть файл этак 70-150 метров
Есть много приложений(потоков)-читателей (они этот файл читают, обрабатывают и выдают резулбтат юзеру)
Есть одно приложение - писатель (он полностью переписывает файл)
Проблема: Сами по себе читатели могут в одновременно читать этот файл
Как тока писатель собрался писать в файл - он должен дождатся завершения уже начавших чтение потоков и не пускать вновь созданых читателей, потом писать в файл (читатели ждут), как тока писатель закончит - разрешить чтение всем читателям...
Как это сделать по уму?
Leonid Troyanovsky (2006-08-09 10:35:00) [1]
> DiamondShark © (08.08.06 23:03) [25]
Если вместо mmf использовать семафор, то получится классический
SWMRG ([4]). Одна из присущих ему проблем в том, что когда
читателей много, то они могут полностью блокировать работу
писателя. Отсюда, например, необходимость в hEventUnlockBeginRead.
Но, в любом случае, для реальных приложений возможностей
подобного объекта явно не хватает.
--
Regards, LVT.
redlord (2006-08-08 23:06:00) [2]не плохо былобы узнать что за инфа лежит в файле а то может перекинуть все на плечи баз данных . пусть они думает ))
я бы зделал когонить главным либо читало либо писало и пусть он заведует доступом и раздает привелегии а если надо то и отрубает конкурентов
DiamondShark (2006-08-08 23:03:00) [3]Может, кому-нибудь понадобится.
Интерпроцессная версия 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.
Джо (2006-08-08 19:55:00) [4]> [23] Пусик © (08.08.06 19:52)
>
> > межпроцессорная
>
> межпроцессная.
Ну да, межпроцессорнаяэто несколько сложнее :)
Пусик (2006-08-08 19:52:00) [5]
> межпроцессорная
межпроцессная.
Пусик (2006-08-08 19:49:00) [6]В принципе, межпроцессорная синхронизация сложнее только в реализации, логически ничего сложного.
Пусик (2006-08-08 19:47:00) [7]А-а... Прошу прощения. Почему-то в голове отложилось, что внутри одного процесса.
DiamondShark (2006-08-08 19:43:00) [8]Хитренькие какие.
Автору между процессами надо синхронизироваться.
Пусик (2006-08-08 19:23:00) [9]Вобщем-то, задача так себе...
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.
tesseract (2006-08-08 16:19:00) [10]
> clickmaker © (08.08.06 15:29) [14]
согласен, открытие файла весьма ёмкая задачка.
А не проще ли накатать отдельный поток для работы с файлом и организовать там потокобезопасную очередь ?
SamProf (2006-08-08 15:38:00) [11]Больше всего меня интересует целостность файла! Он не испоганится?
clickmaker (2006-08-08 15:37:00) [12]
> SamProf © (08.08.06 15:30) [15]
то есть тебе мало?
рекомендую все-таки прислушаться к вышеизложенным советам.
потому что писать циклы while (true) для достукивания к файлам - не есть гуд. Это не ДОС, это многозадачная система
SamProf (2006-08-08 15:30:00) [13]А кроме этого?
clickmaker (2006-08-08 15:29:00) [14]
> Вопрос - могут ли бытьтраблы?
еще какие!
самая маленькая - 100% загрузка проца
SamProf (2006-08-08 15:26:00) [15]У меня вот такое вот решение. Вопрос - могут ли бытьтраблы?
Если читатель ваще не получит доступ у файлу пока писатель пишет - это не стрпшно!
Вот код читателя:
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:07:00) [16]конечно, если читатель настолько зачитается, что забудет обо всем... Не от хорошей же жизни в СУБД всякие таймауты наворачивают...
Сергей М. (2006-08-08 15:02:00) [17]
> DiamondShark © (08.08.06 14:57) [9]
И в том и в другом случае ситуация со вторым пришествием не исключена.
DiamondShark (2006-08-08 15:01:00) [18]
> В первом случае время ожидания -- функция от интенсивности
> запросов
Даже не интенсивности, а всего лишь длительности одного запроса.
МО = 1\2 рабочего цикла
:)
DiamondShark (2006-08-08 14:57:00) [19]
> Равно, заметь, как и сигнала объекта синхронизации - до
> того же самого пришествия)
а вот и нет.
разница между
SyncObj.Acqure(INFINITE)
и
while not SyncObj.Acqure(0) do Sleep;
есть. И существенная.
В первом случае время ожидания -- функция от интенсивности запросов, во втором -- от погоды на Марсе.
У меня по неопытности как-то случилось, что при двух (!) конкурентах один дожидался обслуживания десятки минут (при длительности рабочих циклов порядка сотен миллисекунд). Разумеется, ситуация не воспроизводилась на другой машине :)
> Если совсем уж по уму, то арбитражем доступа к ресурсу (со
> стороны и писателей и читателей) должен заведовать специально
> реализованный сервис
Это и имел в виду ;)
Сергей М. (2006-08-08 14:46:00) [20]
> DiamondShark © (08.08.06 14:27) [6]
Не показатель, конечно, но мелкомягкие в своих офисных "шедеврах" именно так и поступают)