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()
. Но я думаю, что это не будет полным решением проблем.