Форум: "Начинающим";
Текущий архив: 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.052 c