GetMessage с таймаутом

У меня есть приложение, которое второй поток вызывает GetMessage() в цикле. В какой-то момент первый поток понимает, что пользователь хочет выйти из приложения и уведомляет второй поток, который он должен прекратить. Когда второй поток застрял на GetMessage(), программа никогда не завершает работу. Есть ли способ ждать сообщений с таймаутом? Я также открываю другие идеи.

EDIT: (дополнительные пояснения)

Второй поток выполняет этот фрагмент кода:

while ( !m_quit && GetMessage( &msg, NULL, 0, 0 ) )
{
    TranslateMessage( &msg );
    DispatchMessage( &msg );
}

Первый поток устанавливает m_quit в true.

Ответы

Ответ 1

Самый простой способ - просто вызвать UINT_PTR timerId=SetTimer(NULL, NULL, 1000, NULL) перед вызовом GetMessage. Он будет отправлять сообщение WM_TIMER вызывающему потоку каждую секунду, поэтому GetMessage будет возвращаться немедленно. Затем вызовите KillTimer(NULL, timerId), чтобы отменить его.

UPDATE Пример кода:

BOOL GetMessageWithTimeout(MSG *msg, UINT to)
{
    BOOL res;
    UINT_PTR timerId = SetTimer(NULL, NULL, to, NULL);
    res = GetMessage(msg);
    KillTimer(NULL, timerId);
    if (!res)
        return FALSE;
    if (msg->message == WM_TIMER && msg->hwnd == NULL && msg->wParam == timerId)
        return FALSE; //TIMEOUT! You could call SetLastError() or something...
    return TRUE;
}

Ответ 2

Не тестировалось, но вы можете попробовать функцию MsgWaitForMultipleObjects без фактического объекта.

MsgWaitForMultipleObjects(0, NULL, FALSE, timeout, QS_ALLEVENTS);

Если if возвращает WAIT_TIMEOUT, это таймаут, но если он возвращает WAIT_OBJECT_0, вы можете вызвать GetMessage с гарантией, чтобы он не был заблокирован.

Обратите внимание на следующее:

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

Итак, вы должны убедиться, что в последний раз, когда вы вызывали какие-либо функции сообщения, в очереди нет сообщений, или у вас будет какое-то состояние гонки.

Вероятно, лучшим вариантом было бы заменить GetMessage на:

if (MsgWaitForMultipleObjects(0, NULL, FALSE, timeout, QS_ALLEVENTS) == WAIT_OBJECT_0)
{
    while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
    {
        //dispatch the message
    }
}

Но, как я уже говорил, я не тестировал его, поэтому не могу быть уверен, что он сработает.

Ответ 3

Одна вещь, которую вы всегда можете сделать, это просто отправить заблокированный поток пользовательским сообщением, которое заставит его проснуться, обработать сообщение, а затем вернуться к началу цикла. Фактически, было бы проще всего удалить переменную m_quit и вместо этого просто отправить основному потоку сообщение, в котором говорится: "вам нужно уйти сейчас".

Надеюсь, это поможет!

Ответ 4

Вы можете отправить сообщение о выходе во второй поток с помощью PostThreadMessage.

например.

PostThreadMessage(threadid, WM_QUIT, 0, 0);

Вам не нужно читать переменную m_quit во втором потоке, но вы должны проверять наличие ошибок из GetMessage, а также возвращаемое значение FALSE/0, которое является тем, что возвращается, если следующее сообщение является quit.

Ответ 5

Угу. Вместо этого попробуйте PeekMessage(). Но я думаю, что это не будет полным решением проблем.