【问题标题】:Changing a Window's message loop thread更改 Window 的消息循环线程
【发布时间】:2011-05-19 20:16:08
【问题描述】:

最近我尝试将窗口的消息循环放在它自己的线程中,我想知道为什么它从未收到任何消息,但我了解到 Windows 将消息发布到创建窗口的线程。如何在一个线程中创建一个窗口并让另一个线程接收该窗口的消息?我见过 PostThreadMessage 函数,但我相信它还需要创建窗口的线程来监听消息,这正是我试图避免的事情,所以这个函数不是我需要的。

这似乎是一个常见问题,我花了很多时间在谷歌上搜索答案,但找不到答案。

【问题讨论】:

    标签: c++ multithreading winapi message-loop


    【解决方案1】:

    如何在一个窗口中创建一个窗口 线程并导致另一个线程 收到该窗口的消息?

    简单的答案......你不知道。在要处理其消息的线程上创建窗口。如果这是不可能的,那么您需要重新考虑您的方法。

    【讨论】:

    • 有一个复杂的答案,还是只有一个简单的答案?
    • @Epro:恐怕这是唯一的答案。
    • 您建议的这种方法是同时打开多个窗口的程序使用的方法吗?
    • 是的。事实上,在单个线程上运行多个窗口应用程序是完全正常的,而且非常普遍......
    • @Goz:这可能不是唯一的答案,但几乎可以肯定,除了研究项目之外,它是唯一对任何事情都有意义的答案。看我的回答。
    【解决方案2】:

    Windows 消息泵实际上只是一个while 循环,它使用PeekMessage() 从队列中挑选消息并调用您的Windows WndProc 函数。比这还多一点,但这是基本操作。

    因此,while 循环在其中运行的任何线程都是您的窗口可以“运行”的唯一线程。这就是我所见过的每个 Windows 应用程序的构建方式。

    但是我过去认为,只要付出大量努力,就应该可以构建一个具有多线程窗口的 Windows 应用程序。我没有任何代码可以向您展示,因为我已经很久没有考虑过这个问题了,但是我考虑了两种方法:

    1. 在主线程中保留一个消息泵。但是修改消息泵代码,以便在while 中,它根据特定HWND 正在运行的线程使用QueueUserAPC 将消息分派到工作线程。为此需要查找映射,这在计算上可能很昂贵.

    2. 在工作线程中创建一个全新的消息泵。您必须编写所有 while 代码,但这很简单,Petzold's classic book 将为您提供执行此操作所需的所有工具。

    请注意,从架构的角度来看,这种方法对于我能想到的任何应用程序都没有意义。如果您合理地构建您的应用程序,您根本不需要窗口在多个线程中运行。窗口处理发生在一个线程中,操作发生在另一个(或多个)线程中。然而,我认为这是一个有趣的研究领域,这也是我走上这条道路的原因。长话短说,我几乎可以肯定你不需要这样做,也不应该这样做——但这可能就是你应该这样做的方式。

    【讨论】:

      【解决方案3】:

      这是不可能的。每个窗口属于创建它的线程,并且该所有权不能转移。

      这不是将您的消息泵放在另一个线程中的问题。每个线程都有自己的消息队列。当您向窗口发送或发布消息时,操作系统会检查哪个线程拥有该窗口并将消息定向到该线程的消息队列。线程不能读取除自己的消息队列外的任何消息队列,因此您不能让线程处理另一个线程窗口的消息。

      您可以像the first idea in John's answer 那样将消息重新发送到另一个线程,但作为一个通用的消息处理程序,这将变得比它的价值更复杂。许多消息旨在修改窗口的状态,但除了窗口自己的线程之外,您无法修改状态。有些消息是为了获得有意义的返回值而发送的,但是在消息处理完之前你不知道要返回什么,所以你必须阻塞,等待工作线程处理消息。

      您最好确定真正可以卸载到工作线程的一小组消息并专门处理它们。一旦你这样做了,你就不会真正有一个窗口,它的消息在不同的线程中处理。你只会有一个普通的工作线程,而且推理起来不会那么混乱。

      如果有消息被发送到您的窗口,需要大量时间来处理,但发件人不需要知道结果,或者您在完成处理之前就知道结果,然后您可以通过调用ReplyMessage 给予早期响应。这让发送线程继续运行,而您的窗口线程执行额外的工作。

      【讨论】:

        【解决方案4】:

        您可以查看AttachThreadInput 是否适合您 - 它可以让您处理来自其他线程的消息。

        这确实不是一个常见的问题,因为您几乎总是在创建它的线程中处理窗口消息。见 Goz 的回答;我同意。

        请注意,将消息处理放在另一个线程中将一无所获。不要将 GUI 任务分解为多个线程,将处理/后台任务分解为线程。

        【讨论】:

        • 这不是 ATI 所做的。它只允许两个线程共享输入状态。比如焦点和键盘状态。
        • 使用两个单独的线程进行消息处理可以在 UI 中实现更高的并行性。例如,两个不同的窗口可以将消息发布到彼此的线程消息队列中,并且都可以继续正常处理,而对于单个线程,消息的发布和处理将被序列化。在两个单独的线程中处理消息将并行进行,而对于单个线程,则必须串行处理消息。
        【解决方案5】:

        看到这个:

        msdn:http://msdn.microsoft.com/en-us/library/ms644946(v=vs.85).aspx

        消息发布到的线程必须已经创建了消息队列,否则 调用 PostThreadMessage 失败。使用以下方法来处理这种情况。

        1.创建一个事件对象,然后创建线程。

        2.在调用PostThreadMessage之前,使用WaitForSingleObject函数等待事件设置为signaled状态。

        3.在将要发布消息的线程中,调用此处所示的PeekMessage,强制系统创建消息队列。

        PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE)
        

        4.设置事件,表示线程已准备好接收发布消息。

        【讨论】:

        • 这不会从原始窗口中检索消息。
        • 欢迎来到 StackOverflow。感谢您的回答,但它并没有真正回答关于如何将整个处理移动到另一个线程的原始问题。因此,我建议删除此答案。
        猜你喜欢
        • 1970-01-01
        • 2010-09-18
        • 2012-05-14
        • 1970-01-01
        • 2013-01-07
        • 1970-01-01
        • 1970-01-01
        • 2012-05-24
        • 2012-02-07
        相关资源
        最近更新 更多