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

Вниз

Семафоры и события   Найти похожие ветки 

 
panov ©   (2002-04-18 13:19) [0]

Есть такая задача:
В одном приложении работают 2 потока.
Первый поток пишет в TStringList(добавляет записи)( Писатель), а второй поток читает из него и удаляет их( Читатель).

Стоит такая задача - Писатель должен уведомлять Читателя о том, что появились данные для чтения.

Пытаюсь сделать так:
Писатель:

he := CreateEvent(nil,True,True,PChar("TestEvent"));
if he=0 then ShowMessage("Event not created");


в методе при добавлении записей:
SetEvent(he);
Читатель:

hevent := OpenEvent(0,True,PChar("TestEvent"));
ShowMessage(IntToStr(GetLastError));
if he=0 then
begin
ShowMessage("Event not Opened");
Exit;
end;


Далее:

while not <условие> do
begin
WaitForSingleObject(he,1000);
Err := GetLastError;
case Err of
WAIT_FAILED: AddLog("Failed");
WAIT_TIMEOUT: AddLog("WAIT TIMEOUT");
WAIT_OBJECT_0:
begin
<Вызов процедуры чтения и удаления строк>
ResetEvent(he);
end;
end;


Так вот строка hevent := OpenEvent(0,True,PChar("TestEvent"));
не срабатывает.

ShowMessage(IntToStr(GetLastError)); выдает 2

Что я делаю неправильно?


 
panov ©   (2002-04-18 13:21) [1]

Корректировка:
Для Читателя вместо he везде hevent


 
Romkin ©   (2002-04-18 13:30) [2]

По-моему, в OpenEvent нужен флаг EVENT_MODIFY_STATE or EVENT_ALL_ACCESS в первом параметре


 
Anatoly Podgoretsky ©   (2002-04-18 13:37) [3]

Как насчет he := CreateEvent(nil,True,True,PChar("TestEvent"));
Согласно хелпа, оишбка два это ERROR_FILE_NOT_FOUND
И мне кажется, что не правомочно вызывать ShowMessage(IntToStr(GetLastError));, вызови если hevent = 0


 
Digitman ©   (2002-04-18 13:46) [4]

правильнее для "читателя" было бы так :

hevent := OpenEvent(EVENT_ALL_ACCESS, False,PChar("TestEvent"));

Только, imho, этого вообще можно не делать, если и "писатель" и "читатель" работают в АП одного и того же хост-процесса. Касаемо задачи в том виде, в каком ты ее описал, разумней было бы организовать создание Event-объекта где-нибудь в секции иниц-ции и полученный хэндл записать в глобально доступное (для кода "писателя" и "читателя") место в проекте. Тогда никакого OpenEvent() не потребуется : хэндл объекта-события всегда известен обоим и доступен : одному - для сигналинга, другому - для ожидания сигнала.

Однако, если ты говоришь о синхронизации доступа к списку при одновременном обращении к нему по записи и чтению, придется-таки задействовать еще и объект синхронизации. Выбирай на вкус : мьютексы, семафоры, критические секции..





 
panov ©   (2002-04-18 13:47) [5]

>Romkin
Спасибо.
После вызова
he := OpenEvent(EVENT_ALL_ACCESS,False,Pchar("TestEvent"));
все заработало, но пришлось еще одну ошибку исправить:

while not <условие> do
begin
Err := WaitForSingleObject(he,1000);
// Err := GetLastError;

case Err of
WAIT_FAILED: AddLog("Failed");
WAIT_TIMEOUT: AddLog("WAIT TIMEOUT");
WAIT_OBJECT_0:
begin
<Вызов процедуры чтения и удаления строк>
ResetEvent(he);
end;
end;

Исправлены строки жирным шрифтом.

>Anatoly Podgoretsky
Спасибо, тоже исправлю.



 
panov ©   (2002-04-18 13:54) [6]

>Digitman © (18.04.02 13:46)
Да, эти части(запись/чтение) синхронизированы через критические секции, причем таким образом:

Методы в потоке Писателя
procedure TLogDispatcher.AddString(const s: String);
begin
csLog.Enter;
tL.Add(FormatDateTime("dd.mm.yyyy hh.nn.ss",now)+":"+s);
csLog.Leave;
end;

function TLogDispatcher.GetString: String;
begin
csLog.Enter;
if tL.Count>0 then
begin
Result := tL[0];
tL.Delete(0);
end
else Result :="";
csLog.Leave;
end;


В потоке- Читателе обращение идет таким способом:

if Assigned(FDispatcher) then
begin
CurrStr := FDispatcher.GetString;
if Currstr<>"" then LogWrite(CurrStr);
end;


Здесь:
FDispatcher: TLogDispatcher; //Указатель на поток- Писатель


 
Digitman ©   (2002-04-18 14:05) [7]

>panov
Если реализованы, тогда - полный порядок)

Тем не менее пересмотри все же реальную необходимость вызова OpenEvent() - все это у тебя-таки делается в рамках одного приложения: ну, нет просто смысла запрашивать хэндл события о ОС, если он уже известен


 
panov ©   (2002-04-18 14:19) [8]

Кстати, прошу обратить внимание на использование критических секций и обращения к методу function TLogDispatcher.GetString: String; из другого потока. Все ли здесь корректно.

>Digitman © (18.04.02 14:05)
Тем не менее пересмотри все же реальную необходимость вызова OpenEvent()

Задача на самом деле несколько сложнее.

Несколько десятков потоков мониторят каталоги(проверка появления новых файлов) и при появлении нового файла обращаются к потоку- Писателю(его методу AddString) для добавления информации о появлении нового файла.
Для максимальной скорости мониторинга и исключения задержек при записи журнала в файл и введен поток- Писатель и поток- Читатель.

К потоку- писателю могут обращаться одновременно до 40-50 потоков, проверяющих файлы. Запись в TStringList происходит достаточно быстро, пожтому потоки не будут ожидать друг друга.
А вот поток- Читатель выполняет достаточно много дисковых операций, поэтому он не должен задерживать поток- Писатель при выполнении файловых операций.
В то же время мне хотелось бы минимизировать нагрузку на процессор именно этим потоком и не крутить его в цикле с ProcessMessages или Sleep( Sleep - слишком медленно будет обрабатывать записи в потоке- Писателе)

Так же хотелось видеть в Task-Manager"е под NT реальную загрузку процессора этой программой.

Из этого я исходил, вводя событие для синхронизации Event.
В принципе, после доводки до рабочего кода, могу привести его здесь, благо кода немного.
Может быть, тогда больше мчслей возникнет по оптимизации у мастеров.


 
Digitman ©   (2002-04-18 14:50) [9]

>panov

Нет, ну это все замечательно, но - как бы там ни было - какое это отношение имеет к крайней необходимости вызывать всякий раз OpenEvent(), когда "читатель" хочет ждать события ?
Первый же "писатель", модифицировавший список, вызывает CreateEvent() и сохраняет хэндл где-нить в глоб.области данных приложения, после чего вызывает SetEvent() и стартует "читателя". Очередные "писатели", обнаружив, что хэндл создан, уже не озабочены вызовом CreateEvent() и конструированием "читателя", они просто после модификации списка выполняют SetEvent() - и всех делов. Стартовавший "читатель" же в нужный момент времени перед циклом ожидания Event"а обращается к глоб.переменной за хэндлом и ждет его сигнала в цикле.


 
panov ©   (2002-04-18 15:07) [10]

Первый же "писатель", модифицировавший список, вызывает CreateEvent() и сохраняет хэндл где-нить в глоб.области данных приложения,

Т.е. синхронизация для записи/чтения в "глоб. область данных" мне понадобится лишь один раз для писателя и один раз для читателя?
Тогда это приемлимо.

На самом деле и писатель и читатель только один раз выполняют CreateEvent и OpenEvent соответственно.
А далее каждый из них выполняет только SetEvent(писатель), WaitForSingleObject/ResetEvent(Читатель).

Тогда мне все-таки не понятно, для чего выделять Handle в глобальной области, ведь я создаю и открываю объект только один раз?


 
Digitman ©   (2002-04-18 15:14) [11]

Да на кой черт его открывать-то ? Хэндл известен (тот, кто первый вызвал CreateEvent(), записал полученный хэндл в голоб.переменную). Все остальные, кто вызывает любую ф-цию, требующую в кач-ве параметра хэндл события, просто берут значение этого параметра из той самой глоб.переменной - и все ! Какие проблемы-то ?


 
panov ©   (2002-04-18 15:16) [12]

Digitman © (18.04.02 15:14)
т.е. OpenEvent делать не надо? Наконец-то понял-)


 
Digitman ©   (2002-04-18 16:06) [13]

>panov
))))))))))))))



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

Текущий архив: 2002.06.20;
Скачать: CL | DM;

Наверх




Память: 0.51 MB
Время: 0.009 c
1-65045
Explorer
2002-06-06 08:29
2002.06.20
FastReport VCL и CLX


3-64985
Pavel_S
2002-05-28 15:55
2002.06.20
Оптимизация программы


1-65022
Igorek
2002-06-08 23:38
2002.06.20
Как из TCommandClass вытянуть реальный класс


1-65053
nitro313
2002-06-09 03:34
2002.06.20
Как динамически создавать таблицы и dbgrid ?


1-65096
MystiX
2002-06-06 19:40
2002.06.20
Причтите PLZ!