Пробуждение потока с использованием объектов ядра или флагов состояния очереди
Функции GetMessage и PeekMessage приостанавливают поток до тех пор, пока ему не понадобится выполнить какую-нибудь задачу, связанную с пользовательским интер фейсом. Иногда то же самое было бы удобно и при обработке других задач. Для этого поток должен как-то узнавать о завершении операции, не относящейся к пользова тельскому интерфейсу.
Чтобы поток ждал собственных сообщений, вызовите функцию MsgWaitForMultiple Objects или MsgWaitForMultipleObjectsEx:
DWORD MsgWaitForMultipleOb]ects( DWORD nCount, PHANDLE phOb]ects, BOOL fWaitAll, DWORD dwMilUseconds, DWORD dwWakeMask);
DWORD MsgWaitForMultipleObjectsEx( DWORD nCount, PHANDLE phObjects, DWORD dwMilUseconds,DWORD dwWakeMask, DWORD dwFlags);
Эти функции аналогичны WaitForMultipleObjects (см главу 9). Разница в том, что при их использовании поток становится планируемым, когда освобождается какой нибудь из указанных объектов ядра или когда оконное сообщение нужно переслать окну, созданному этим потоком.
Внутренне система просто добавляет объект ядра "событие" в массив описателей ядра Параметр dwWakeMask сообщает системе, в какой момент объект-событие дол жно переходить R свободное состояние. Его допустимые значения идентичны тем, которые можно передавать в функцию GetQueueStatus.
WaitForMultipleObjects обычно возвращает индекс освобожденного объекта (в диа пазоне от WAIT_OBJECT_0 до WAIT_OBJECT_0 + nCount - 1). Задание параметра dwWa keMask равносильно добавлению еще одного описателя При выполнении условия, определенного маской пробуждения,MsgWaitForMullipleObjects(Ex) возвращает значе ние WAIT_OBJECT_0 + nCount.
Вот пример вызова MsgWaitForMultipleObjects
MsgWaitForMultipleObjects(0, NULL, TRUE, INFINITE, QS_INPUT);
Описатели синхронизирующих объектов в этом операторе не передаются — па раметры nCount и phObjects равны соответственно 0 и NULL. Мы указываем функции ждать освобождения всех объектов Но в действительности задан лишь один объект, и с тем же успехом параметру fWaitAll можно было бы присвоить знячение FALSE. Мы также сообщаем, что будем ждать — сколько бы времени это ни потребовало — появ ления в очереди ввода потока сообщения от клавиатуры или мыши.
Начав пользоваться функцией MsgWaitForMultipleObjects в своих программах, Вы быстро поймете, что она лишена многих важных качеств. Вот почему Microsoft при шлось создать более совершенную функцию MsgWaitForMultipleObjectsEx, которая по зволяет задать в параметре dwFlags любую комбинацию следующих флагов.
Флаг | Описание |
MWMO_WAITALL | Функция ждет освобождения всех объектов ядра и появления в очереди потока указанных сообщений (без этого флага функ ция ждет освобождения одного из объектов ядра или появле ния в очереди одного из указанных сообщений) |
MWMO_ALERTABLE | Функция ждет в «тревожном» состоянии |
MWMO_INPUTAVAILABLE | Функция ждет появления в очереди потока одного из указан ных сообщений |
При использовании MsgWaitForMultipIeObjects(Ex) учитывайте, что.
Заметьте, что и последняя особенность этих функций — не очень приятный сюр приз для многих разработчиков. Возьмем простой пример Допустим, в очереди по токи находятся два сообщения о нажатии клавиш. Если теперь вызвать MsgWaitForMul tipleObjects(Ex) и задать в dwWakeMask значение QS_INPUT, поток пробудится, извле чет из очереди первое сообщение и обработает его Но на повторный вызов MsgWait ForMultipleObjects(Ex) поток никак не отреагирует — ведь новых сообщений в очере ди нет.
Этот механизм создал столько проблем разработчикам, что Microsoft пришлось добавить в MsgWaitForMultipleObjectsEx поддержку флага MWMO_INPUTAVATLABLE
Вот как надо писать цикл выборки сообщений при использовании MsgWaitForMul tipleObjectsEx
BOOL fQuit = FALSE; // надо ли завершить цикл?
while (!fQuit)
{
// поток пробуждается при освобождении обьекта ядра ИЛИ
// для обработки сообщения от пользовательского интерфейса
DWORD dwResult = MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, QS_ALLEVENTS, MWMO_INPUTAVAILABLE);
switch (dwResult}
{
case WAIT_OBJECT_0:
// освободилось событие
break;
case WAIT_OBJECT_0 + 1:
// в очереди появилось сообщение
// разослать все сообщения MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
// сообщение WM_QUIT - выходим из цикла
fQuit = TRUE;
}
else
{
// транслируем и пересылаем сообщение
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// наша очередь пуста
break;
}
}
// конец цикла while