Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Создание эффективных приложений для Windows Джеффри Рихтер 2004 (Книга).pdf
Скачиваний:
376
Добавлен:
15.06.2014
Размер:
8.44 Mб
Скачать

WaitForSingleObject(g htvent, INFINITE);

// обращаемся к блоку памяти

SetEvent(g_hEvent); return(0);

}

DWORD WINAPI GrammarCheck(PVOID pvParam)

{

//ждем, когда в память будут загружены данные из файла

WaitForSingleObject(g_hEvent, INFINITF);

//обращаемся к блоку памяти

SetEvent(g_hEvent);

return(0);

}

Закончив свою работу с данными, поток вызывает SetEvent, которая разрешает системе возобновить выполнение следующего из двух ждущих потоков. И опять мы не знаем, какой поток выберет система, но так или иначе кто-то из них получит мо нопольный доступ к тому же блоку памяти. Когда и этот поток закончит свою работу, он тоже вызовет SetEvent, после чего с блоком памяти сможет монопольно опериро вать третий, последний поток Обратите внимание, что использование события с ав тосбросом снимает проблему с доступом вторичных потоков к памяти как для чте ния, так и для записи; Вам больше не нужно ограничивать их доступ только чтением. Этот пример четко иллюстрирует различия в применении событий со сбросом вруч ную и с автосбросом.

Для полноты картины упомяну о еще одной функции, которую можно использо вать с объектами-событиями

BOOL PulseEvent(HANDLE hEvent);

PuteeEvent освобождает событие и тут жс переводит его обратно в занятое состо яние; ее вызов равнозначен последовательному вызову SelEvent и ResetEvent. Если Вы вызываете PulseEvent для события со сбросом вручную, любые потоки, ждущие этот объект; становятся планируемыми. При вызове этой функции применительно к со бытию с автосбросом пробуждается только одип из ждущих потоков. А если ни один из потоков не ждет объект-событие, вызов функции не дает никакого эффекта

Особой пользы от PulseEuent я не вижу В сущности, я никогда не пользовался ею на практике, потому что абсолютно неясно, какой из потоков заметит этот импульс и станет планируемым Наверное, в каких-то сценариях PulseEuent можст пригодиться, но ничего такого мне в голову не приходит Когда мы перейдем к рассмотрению фун кции SignalObjectAndWait, я расскажу о PulseEvent чуть подробнее.

Программа-пример Handshake

Этa программа, «09 Handshakeexe" (см листинг на рис 9-1), демонстрирует приме нение событий с автосбросом. Файлы исходного кода и ресурсов этой программы находятся в каталоге 09-Handshake" на компакт-диске, прилагаемом к книге. После запуска Handshake открывается окно, показанное ниже.

Handshake принимает строку запроса, меняет в ней порядок всех символов и по казывает результат в поле Result. Самое интересное в программе Handshake — то, как она выполняет эту героическую задачу

Программа решает типичную проблему программирования У Вас есть клиент и сервер, которые должны как-то общаться друг с другом. Изначально серверу делать нечего, и он переходит в состояние ожидания Когда клиент готов передать ему зап рос, он помещает этот запрос в разделяемый блок памяти и переводит объект-собы тие в свободное состояние, чтобы поток сервера считал этот блок памяти и обрабо тал клиентский запрос Пока серверный поток занят обработкой запроса, клиентский должен ждать, когда будет готов результат Поэтому клиент переходит в состояние ожидания и остается в нем до тех пор, пока сервер не освободитдругой объект-со бытие, указав тем самым, что результат готов Вновь пробудившись, клиент узнает, что результат находится в разделяемом блоке памяти, и выводит готовые данные пользо вателю.

При запуске программа немедленно создает два объекта-события с автосбросом в занятом состоянии Один ит них, g_hevtRequestSubmitted, используется как индика тор готовности запроса к серверу. Этo собьпие ожидается серверным потоком и ос вобождается клиентским. Второй обьект-событие, g_hevtRequestSubmitted, служит инди катором готовности данных для клиента. Это событие ожидается клиентским пото ком, а освобождается серверным.

После создания событий программа порождает серверный поток и выполняет функцию ServerThread Эта функция немедленно заставляет серверный поток ждать запроса от клиента. Тем временем первичный поток, который одновременно являет ся и клиентским, вызывает функцию DialogBox, отвечающую за отображение пользо вательского интерфейса программы Вы вводите какой-нибудь текст в поле Request и, щелкнув кнопку Subrnit Request To Server, заставляете программу поместить строку запроса в буфер памяти, разделяемый между клиентским и серверным потоками, а также перевести событие g_hevtRequestSubmitted в свободное состояние Далее клиен тский поток ждет результат от сервера, используя объект-событие g_hevtResultReturned

Теперь пробуждается серверный поток, обращает строку в блоке разделяемой па мяти, освобождает событие g_hevtResultReturned и вновь засыпает, ожидая очередно го запроса от клиента. Заметьте, что программа никогда не вызывает ResetEvent, так как в этом нет необходимости; события с автосбросом автоматически восстанавли вают свое исходное (занятое) состояние в результате успешного ожидания Клиентс кий поток обнаруживает, что событие g_hevtResultReturned освободилось, пробужда ется и копирует строку из общего буфера памяти в поле Result.

Последнее, что заслуживает внимания в этой программе, — то, как она заверша ется Вы закрываете ее окно, и это приводит к тому, что DialogBox в функции _tWinMain возвращает управление. Тогда первичный поток копирует в общий буфер специаль ную