【问题标题】:Why shouldn't gui threads live in multithreaded apartments?为什么 gui 线程不应该存在于多线程单元中?
【发布时间】:2014-10-31 09:34:46
【问题描述】:

COINIT - 一个用于指定 Windows 线程是在单线程还是多线程单元中的枚举 - 文档 (http://msdn.microsoft.com/en-gb/library/windows/desktop/ms678505(v=vs.85).aspx) 指出:

多线程单元旨在供非 GUI 线程使用。多线程单元中的线程不应执行 UI 操作。这是因为 UI 线程需要消息泵,而 COM 不会为多线程单元中的线程泵送消息。

为什么多线程单元中的线程不应该执行 UI 操作?在多线程单元中的线程中有消息循环有什么问题? COM 是否以某种方式为单线程单元中的线程提供了自动消息循环?

【问题讨论】:

  • 您可以创建一个线程,让它加入 MTA,然后在其上运行消息泵;没有什么能阻止你。然而,这通常是没有意义的。如果您在 COM 服务器中执行此操作,并且客户端调用您的 COM 对象上的方法,则此调用将在 不同 MTA 线程(来自 COM 运行时管理的池中的一个)上执行。此外,如果您在此线程上进行公寓外 COM 调用,则该调用将阻塞并且您的 UI 将显示为挂起(而来自 STA 线程的公寓外 COM 调用会在等待调用返回)。
  • 是的 - 当 COM 代表您创建 STA 线程时,该线程会运行消息泵。当 MTA 线程创建进程内 STA COM 对象时会发生这种情况。
  • "在多线程单元的线程中存在消息循环有什么问题?"那不是问题。问题是(正如您在引用的文档中所说)“COM 不会为多线程单元中的线程泵送消息”。这意味着如果 COM 需要等待,它将等待而不发送消息,这会挂起您的 UI。 COM 在单线程单元中等待时会发送消息。
  • 所以@IgorTandetnik 当您从 STA 线程调用公寓外 COM 对象时,您的正常消息循环(或其他一些消息循环)会在等待时运行吗?如果是后者,这是否记录在任何地方?
  • 参见INFO: Descriptions and Workings of OLE Threading Models:“客户端的线程在进行传出呼叫时进入 COM 提供的消息循环。”另见CoRegisterMessageFilter

标签: c++ windows multithreading com


【解决方案1】:

这有点倒退,UI 线程主要需要一个消息循环,以便它可以接收来自 Windows 和其他进程的通知。消息循环是producer-consumer problem 的通用解决方案。随着操作系统和其他进程的产生,UI线程正在消耗。

UI 线程使用大量非线程安全的代码。这包括在 COM 中实现的主要功能,如拖放、剪贴板、外壳对话框、ActiveX 控件(如浏览器)。还有一大堆代码从来没有成为线程安全的,因为程序员不必这样做,更容易编写。这些功能需要一个 STA 线程,即通过将 COINIT_APARTMENTTHREADED 传递给 CoInitializeEx() 来初始化 COM 的线程。

这是对 COM 的承诺,线程将成为一个好公民,不允许进行阻塞调用,并且必须泵送消息循环。它是 COM 用来编组从工作线程到 STA 线程的调用以保持 COM 对象线程安全的消息循环。当所有调用都来自同一个线程时,就不会出现安全问题。底层调用是 SendMessage(),有大量的管道将函数参数从一个堆栈复制到另一个堆栈。 CoInitializeEx() 创建一个隐藏的消息窗口,该窗口由处理消息并实际进行调用的 STA 线程拥有。安全。

【讨论】:

    猜你喜欢
    • 2012-09-21
    • 2014-07-21
    • 2013-11-22
    • 2015-07-09
    • 1970-01-01
    • 2016-06-10
    • 2018-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多