Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "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
1-65101
Демон
2002-06-07 01:17
2002.06.20
2 вопроса про Таймер!!!


4-65301
panov
2002-04-18 13:19
2002.06.20
Семафоры и события


14-65206
ATLANTIDO
2002-05-16 15:59
2002.06.20
Формирование отчетов в Excel


1-65031
MSergey
2002-06-09 16:20
2002.06.20
TListView


6-65189
wed
2002-04-05 16:06
2002.06.20
Сообщение сервера приложений клиенту





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский