【问题标题】:GetMessage with a timeoutGetMessage 超时
【发布时间】:2012-06-02 22:08:01
【问题描述】:

我有一个应用程序,第二个线程在循环中调用GetMessage()。在某些时候,第一个线程意识到用户想要退出应用程序并通知第二个线程它应该终止。由于第二个线程卡在GetMessage(),程序永远不会退出。有没有办法等待超时消息? 我也对其他想法持开放态度。

编辑:(附加说明)

第二个线程运行sn-p的代码:

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

第一个线程将m_quit 设置为true。

【问题讨论】:

  • 等待...哪个线程在循环中等待GetMessage()?第一个线程,第二个线程,还是两者都有?
  • 第二个线程运行ˇGetMessageˇ循环。我应该发布一些代码吗?
  • 代码总是有帮助的。我只是感到困惑,因为您的问题似乎使两个线程都被阻塞了。您不能只发送一条自定义消息说“您现在需要关闭吗?”
  • 这个问题很有趣,但我不明白你为什么需要它。一旦主线程愿意退出,它只需要通知所有线程完成(PostQuitMessage是你的朋友),然后等待它们完成(WaitForSingleObjectWaitForMultipleObjects)然后自己完成.
  • @rodrigo 主要是因为我对 Windows API 不太了解 :) 如果这是推荐的做事方式,我会改成那样。

标签: c++ windows winapi


【解决方案1】:

未测试,但您可以尝试函数MsgWaitForMultipleObjects,实际上没有任何对象。

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

如果如果返回WAIT_TIMEOUT,则表示超时,但如果返回WAIT_OBJECT_0,则可以调用GetMessage,保证不会被阻塞。

但请注意以下几点:

如果线程调用函数检查队列后消息队列中存在指定类型的未读输入,则 MsgWaitForMultipleObjects 不会返回。

因此,您必须确保上次调用任何消息函数时队列中没有消息,否则您将遇到一种竞争条件。

您最好的选择可能是将GetMessage 替换为:

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

但正如我之前所说,我没有测试它,所以我不确定它是否会起作用。

【讨论】:

  • 我在等待 SendMessage 消息时遇到了死锁。您应该使用 QS_ALLINPUT 而不是 QS_ALLEVENTS,因为它也涵盖了 QS_SENDMESSAGE。请参阅msdn 了解更多信息
  • 感谢@5andr0 QS_ALLINPUT 为我解决了这个问题!
  • 另外,如果你将 bWaitAll 设置为 TRUE,对你来说会很危险,正如我发现的那样。此处还包括:blogs.msdn.microsoft.com/larryosterman/2004/06/02/…
【解决方案2】:

最简单的方法是在调用GetMessage 之前调用UINT_PTR timerId=SetTimer(NULL, NULL, 1000, NULL)。它将每秒向调用线程发送一条WM_TIMER 消息,因此GetMessage 将立即返回。然后拨打KillTimer(NULL, timerId)取消。

更新示例代码:

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;
}

【讨论】:

  • @rodrigo:+1。到目前为止我见过的最好的解决方案。我会等待其他候选人出现。
  • @rodrigo 注意msg 是一个指针。另外,指向的结构没有uMsg 成员,它被称为message
【解决方案3】:

您始终可以做的一件事就是向被阻塞的线程发送一条用户定义的消息,使其唤醒,处理该消息,然后返回到循环的顶部。事实上,最简单的方法可能是完全删除 m_quit 变量,而只是向主线程发送一条消息,说“你现在需要退出”。

希望这会有所帮助!

【讨论】:

  • +1,这有帮助。我更喜欢 rodrigo 的解决方案,因为它将整个戒烟机制集中在一个地方。
  • 这是一种更简洁的方法。线程的重点是接收告诉它要做什么的消息。而且这不需要线程在无事可做时不断唤醒。
【解决方案4】:

您应该可以使用PostThreadMessage 向第二个线程发布退出消息。

例如

PostThreadMessage(threadid, WM_QUIT, 0, 0);

您不需要在第二个线程中读取m_quit 变量,但您应该检查来自GetMessage 的错误以及FALSE/0 的返回值,如果下一条消息是退出消息。

【讨论】:

    【解决方案5】:

    是的。请改用PeekMessage()。但我认为这不会完全解决问题。

    【讨论】:

    • @Forgoth 调用PeekMessage() 将是一个很好的解决方案,如果我可以将它与Sleep(1000) 结合使用,但在我的情况下,第二个线程应该是非常被动的。 +1,因为该解决方案在其他情况下有效
    猜你喜欢
    • 2018-07-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多