Функция SignalObjectAndWait
SignalObjectAndWait переводит в свободное состояние один объект ядра и ждет дру гой объеют ядра, выполняя все это как одну операцию на уровне атомарного доступа:
DWORD SignalObjectAndWait( HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds, BOOL fAlertable);
Параметр hObjectToSignal должен идентифицировать мьютекс, семафор или собы тие; объекты любого другого типа заставят SignalObjectAndWait вернуть WAIT_FAILED, а функцию GetLastError — ERROR_INVALIDHANDLE. Функция SignalObjectAndWait про веряет тип объекта и выполняет действия, аналогичные тем, которые предпринима ют функции ReleaseMutex, ReleaseSemaphore (со счетчиком, равным 1) или ResetEvent.
Параметр hObjectToWaitOn идентифицирует любой из следующих объектов ядра: мьютекс, семафор, событие, таймер, процесс, поток, задание, уведомление об изме нении файла или консольный ввод. Параметр dwMilliseconds, как обычно, определяет, сколько времени функция будет ждать освобождения объекта, a флаг fAlertable указы вает, сможет ли поток в процессе ожидания обрабатывать посылаемые ему АРС-вы зовы.
Функция возвращает одно из следующих значений: WAIT_OBJECT_0, WAIT_TIME OUT, WAIT_FAILED, WATT_ABANDONED (см. раздел о мьютексах) или WAIT_IO_COMP LETION.
SignalObjectAndWait — удачное добавление к Windows AFI по двум причинам. Bo псрвых, освобождение одного объекта и ожидание другого — задача весьма распро страненная, а значит, объединение двух операций в одной функции экономит про цессорное время. Каждый вызов функции, заставляющей поток переходить из кода, который работает в пользовательском режиме, в код, работающий в режиме ядра, требует примерно 1000 процессорных тактов (на платформах x86), и поэтому для выполнения, например, такого кода:
ReleaseMutex(hMutex); WaitForSingleObject{hEvent, INFINITE);
понадобится около 2000 тактов. В высокопроизводительных серверных приложени ях SignalObjectAndWait дает заметную экономию процессорного времени.
Во-вторых, без функции SignalObjectAndWait ни у одного потока не было бы воз можности узнать, что другой поток перешел в состояние ожидания.
Знание таких вещей очень полеяно для функций типа PulseEvent. Как я уже говорил в этой главе, PulseEvenl переводит событие в свободное состояние и тут же сбрасывает его. Если ни один из потоков не ждет данный объект, событие не зафиксирует этот импульс (pulse). Я встречал программистов, которые пишут вот такой код:
// выполняем какие-то операции
...
SetEvent(hEventWorkerThreadDone);
WaitForSingleObject(hEventMoreWorkToBeDone, INFINITE);
// выполняем еще какие-то операции
...
Этот фрагмент кода выполняется рабочим потоком, который проделывает какие то операции, а затем вызывает SetEvent, чтобы сообщить (другому потоку) об оконча нии своих операций. В то же время в другом потоке имеется код:
WaitForSingleObject(hEventWorkerTnreadDone); PulseEvent(hEventMoreWorkToBeDone);
Приведенный ранее фрагмент кода рабочего потока порочен по самой своей сути, так как будет работать ненадежно. Ведь вполне вероятно, что после того, как рабо чий поток обратится к SetEvent, немедленно пробудится другой поток и вызовет Pulse Event. Проблема здесь в том, что рабочий поток уже вытеснен и пока еще не получил шанса на возврат из вызова SetEvent, не говоря уж о вызове WaitForSingleObject. В итоге рабочий поток не сможет своевременно освободить событие bEventMoreWorkToBeDone
Но если Вы перепишете код рабочего потока с использованием функции Signal ObjectAndWait
// выполняем какие-то операции
SignalObjectAndWait(hEventWorkerThreadDone, hEventMoreWorkToBflDonc, INFINITE, FALSE);
// выполняем еще какие-то операции
то код будет работать надежно, поскольку освобождение и ожидание реализуются на уровне атомарного доступа. И когда пробудится другой поток, Вы сможете быть абсолютно уверены, что рабочий поток ждет события hEventMoreWorkToBeDone, а значит, он обязательно заметит импульс, "приложенный" к событию.
WINDOWS 98
В Windows 98 функция SignalObjectAndWait определена, но не реализована.