【问题标题】:MessageLoop within Thread线程内的消息循环
【发布时间】:2012-12-18 11:17:19
【问题描述】:

如何使用 OTL 在线程中实现消息循环? Application.ProcessMessages;是我到目前为止使用的,但使用它不是很安全。

谢谢

【问题讨论】:

    标签: delphi delphi-xe2


    【解决方案1】:

    这就是我从线程队列中提取消息的方式:

    while GetMessage(Msg, 0, 0, 0) and not Terminated do begin
      Try
        TranslateMessage(Msg);
        DispatchMessage(Msg);
      Except
        Application.HandleException(Self);
      End;
    end;
    

    使用Application.ProcessMessages 将拉取调用线程队列的消息。但它不适合在消息循环中使用,因为它不会阻塞。这就是您使用GetMessage 的原因。如果队列为空,它会阻塞。并且Application.ProcessMessages 还调用其他并非设计为线程安全的TApplication 方法。所以有很多理由不从主线程以外的线程调用它。

    当你需要终止线程时,我会这样做:

    Terminate;
    PostThreadMessage(ThreadID, WM_NULL, 0, 0);
    //wake the thread so that it can notice that it has terminated
    

    这些都不是 OTL 特定的。这段代码都打算存在于TThread 后代中。但是,这些想法是可以转移的。


    在 cmets 中,您表明您想要运行一个繁忙的、非阻塞的消息循环。你可以使用PeekMessage

    while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
      Try
        TranslateMessage(Msg);
        DispatchMessage(Msg);
      Except
        Application.HandleException(Self);
      End;
    end;
    

    【讨论】:

    • 这表面上看起来很复杂。可以放在程序里吗?像这样:" while IHTMLDocument2.readyState 'complete' do MessageLoop;"
    • 那将是一个繁忙的循环,有些不同。我的更新显示了如何使用PeekMessage 做到这一点。在理想的世界里,你不会那样做。繁忙的循环不必要地消耗 CPU 周期。在理想情况下,您会收到通知,说明您的文档已准备就绪。
    • @DavidHeffernan 看来while GetMessage 不是推荐的方式? msdn.microsoft.com/en-us/library/windows/desktop/…
    • @SOuser 不,该代码很好。有什么问题?
    【解决方案2】:

    您可以构建一个类似于 Application.Run 中的循环。

    您可以致电PeekMessage,它会检查消息是否可用。 PeekMessage 检查当前线程的消息队列,因此如果您在线程中使用它,它会检查线程的消息队列(您可以使用 PostThreadMessage 向其发布消息)。

    除了 PeekMessage,您还可以使用 GetMessage,它会一直等到收到消息。 GetMessage 返回 0 返回 false*)

    当它收到WM_QUIT 消息时,这也是终止消息循环的信号。之后再次调用 GetMessage 是有风险的,因为它可能不会收到另一条消息,并且可能会阻止您的应用程序正常关闭,因为它正在阻塞。

    Application.ProcessMessages 确实不是很安全,因为它执行了许多特定于主线程的附加功能。一方面,它会在消息队列为空时立即触发 Application.OnIdle,这意味着它在线程消息队列为空时调用(问题 1)并且(问题 2)在线程上下文中调用,不允许任何事件中的 VCL 交互。

    *) 关于GetMessage 的返回值:我注意到Delphi 将GetMessage 实现为返回一个LongBool。然而,实际的返回值是一个整数。在 WM_QUIT 的情况下返回 0,在错误的情况下返回 -1,在其他情况下返回非零。 Microsoft states:

    因为返回值可以是非零、零或 -1,所以避免使用类似的代码 这个:

    while (GetMessage( lpMsg, hWnd, 0, 0)) ...
    

    不幸的是,您必须以不同的别名导入 GetMessage 才能正确使用它。

    【讨论】:

    • 与其重新导入,不如直接输入 GetMessage 的结果。
    猜你喜欢
    • 2010-09-18
    • 2011-05-19
    • 2012-05-14
    • 1970-01-01
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    • 2012-05-24
    • 2012-02-07
    相关资源
    最近更新 更多