Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Семафоры и события [D5, Win95/98, WinME, NT4, Win2k]


panov ©   (2002-04-18 13:19:00) 

Есть такая задача:
В одном приложении работают 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

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


Digitman   (2002-04-18 16:06:00) [1]

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


panov   (2002-04-18 15:16:00) [2]

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


Digitman   (2002-04-18 15:14:00) [3]

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


panov   (2002-04-18 15:07:00) [4]

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

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

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

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


Digitman   (2002-04-18 14:50:00) [5]

>panov

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


panov   (2002-04-18 14:19:00) [6]

Кстати, прошу обратить внимание на использование критических секций и обращения к методу 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:05:00) [7]

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

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


panov   (2002-04-18 13:54:00) [8]

>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; //Указатель на поток- Писатель


panov   (2002-04-18 13:47:00) [9]

>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
Спасибо, тоже исправлю.


Digitman   (2002-04-18 13:46:00) [10]

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

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

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

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




Anatoly Podgoretsky   (2002-04-18 13:37:00) [11]

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


Romkin   (2002-04-18 13:30:00) [12]

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


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

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


Наверх

Титульная страница Поиск, карта сайта Написать письмо