为什么GUI线程不应该存在于多线程公寓中?

5
COINIT是一个枚举值,用于指定Windows线程是否在单线程或多线程的公寓中。文档(http://msdn.microsoft.com/en-gb/library/windows/desktop/ms678505(v=vs.85).aspx)说明:

多线程公寓旨在供非GUI线程使用,该公寓内的线程不应执行UI动作。这是因为UI线程需要消息泵,而COM不会为多线程公寓中的线程提供消息泵。

为什么多线程公寓中的线程不应执行UI动作?在多线程公寓中的线程中有一个消息循环有什么问题吗?COM是否会为单线程公寓中的线程提供自动消息循环?


1
你可以创建一个线程,让它加入MTA,然后在上面运行消息泵;没有什么能阻止你。但是,这通常是毫无意义的。如果你在COM服务器中这样做,并且客户端调用了你的COM对象上的方法,则此调用将在不同的 MTA线程上执行(来自COM运行时管理的池中的线程)。此外,如果你在此线程上进行跨单元调用,该调用将会阻塞,导致你的UI看起来像是挂起了(而在STA线程中进行跨单元调用则会在等待调用返回时旋转模态消息泵)。 - Igor Tandetnik
1
是的 - 当COM代表您创建STA线程时,该线程运行消息泵。当MTA线程创建进程内STA COM对象时会发生这种情况。 - Igor Tandetnik
在线程中有一个消息循环,在多线程公寓中会有什么问题吗?这不是问题所在。问题在于(正如你引用的文档所说)“COM不会为多线程公寓中的线程泵送消息”。这意味着,如果COM需要等待,它将在不泵送消息的情况下等待,这会挂起你的UI。而在单线程公寓中等待时,COM会泵送消息。 - Raymond Chen
那么,@IgorTandetnik,当您从STA线程调用一个不在公寓中的COM对象时,您的正常消息循环(或其他消息循环)会在等待时运行吗?如果是后者,这是否有任何文档记录? - McDuffin
请参阅INFO:OLE线程模型的描述和工作原理:“当客户端进行传出调用时,其线程进入由COM提供的消息循环。”另请参阅CoRegisterMessageFilter - Igor Tandetnik
1个回答

5

这有点反向,UI线程主要需要一个消息循环以便从Windows和其他进程接收通知。消息循环是生产者-消费者问题的通用解决方案。操作系统和其他进程产生,UI线程消耗。

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

这是对COM的承诺,保证线程表现良好,不允许进行阻塞调用,并必须使用消息循环。正是这个消息循环被COM用来在工作线程和STA线程之间进行调用的封送以保持COM对象的线程安全。当所有调用都来自同一线程时,就不会存在安全问题。底层调用是SendMessage(),需要进行大量的管道操作以将函数参数从一个堆栈复制到另一个堆栈。CoInitializeEx()创建了一个由STA线程拥有的隐藏消息窗口,该窗口处理消息并实际进行调用。安全可靠。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接