Форум: "WinAPI";
Текущий архив: 2002.06.20;
Скачать: [xml.tar.bz2];
ВнизСемафоры и события Найти похожие ветки
← →
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 вся ветка
Форум: "WinAPI";
Текущий архив: 2002.06.20;
Скачать: [xml.tar.bz2];
Память: 0.49 MB
Время: 0.005 c