Windows для профессионалов

       

Пробуждение потока с использованием объектов ядра или флагов состояния очереди


Функции 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 Функция ждет появления в очереди потока одного из указан ных сообщений
Если Вам не нужны эти дополнительные возможности, передайте в dwFlags нуле вое значение.

При использовании MsgWaitForMultipIeObjects(Ex) учитывайте, что.

  • эти функции лишь включают описатель внутреннего объекта ядра "событие" в массив описателей объектов ядра, и значение параметра nCount не должно превышать 63 (MAXIMUM_WAIT_OBJECTS - 1);

  • если в параметре fWaitAll передастся FALSE, функции возвращают управление при освобождении объекта ядра или при появлении в очереди потока сооб щения заданного типа,

  • если в параметре fWaitAll передается TRUE, функции возвращают управление при освобождении всех объектов ядра и появлении в очереди потока сообщения заданного типа. Такое поведение этих функций преподносит сюрприз многим разработчикам Ведь очень часто поток надо пробуждать при освобож дении всех объектов ядра или при появлении сообщения указанного типа. Но функции, действующей именно так, нет;

  • при вызове любая из этих функций на самом деле проверяет в очереди пото ка только новые сообщения заданного типа.


  • Заметьте, что и последняя особенность этих функций — не очень приятный сюр приз для многих разработчиков.


    Возьмем простой пример Допустим, в очереди по токи находятся два сообщения о нажатии клавиш. Если теперь вызвать 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


    Содержание раздела