假人的ApartmentState


Answers:


234

COM是.NET的祖父。他们有很高的目标,COM所做的事情之一,但.NET完全跳过了,它为类提供了线程保证。COM类可以发布它具有什么样的线程要求。并且COM基础结构可确保满足这些要求。

.NET中完全没有此功能。例如,您可以在多个线程中使用Queue <>对象,但是如果未正确锁定,则代码中将出现一个令人讨厌的错误,很难诊断。

COM线程的详细信息太大,无法放入帖子中。我将重点介绍您的问题。创建COM对象的线程必须告诉COM它希望为具有受限线程选项的COM类提供什么样的支持。这些类中的绝大多数仅支持所谓的Apartment线程,它们的接口方法只能从创建该实例的同一线程中安全地调用。换句话说,他们宣布“无论如何我都不支持线程,请当心不要从错误的线程打电话给我”。即使客户端代码确实确实是从另一个线程调用它的。

有两种,STA(单线程公寓)和MTA。它在CoInitializeEx()调用中指定,该函数必须由对COM进行任何操作的任何线程调用。每当启动线程时,CLR都会自动进行该调用。对于程序的主启动线程,它从Main()方法上的[STAThread]或[MTAThread]属性获取要传递的值。默认值为MTA。对于您自己创建的线程,取决于调用SetApartmentState()的方式。默认值为MTA。线程池线程始终是MTA,无法更改。

Windows中有很多需要STA的代码。您将使用的著名示例是剪贴板,拖放和外壳对话框(如OpenFileDialog)。还有很多您看不到的代码,例如UI Automation程序和观察消息的钩子。这些代码都不必是线程安全的,它的作者在不知道使用哪个程序的情况下很难使其安全。因此,与创建窗口的任何线程一样,WPF或Windows Forms项目的UI线程必须始终为STA以支持此类代码。

您向COM承诺您的线程是STA,但是确实要求您遵循单线程单元协定。它们非常僵硬,当您违反合同时,您可能会很难诊断问题。要求是您绝不会在任何时间段内阻塞线程,并且泵送消息循环。WPF或Winforms的UI线程可以满足后者的要求,但是如果您创建自己的STA线程,则需要自己进行维护。打破合同的常见诊断是死锁。

内置了很多CLR支持这些需求,帮助您摆脱麻烦。该声明和WaitOne的()方法泵消息循环,当它在一个STA线程块。但是,这仅满足了永不阻塞的要求,您仍然需要创建自己的消息循环。WPF和Winforms中的Application.Run()。

我以前提供了一个答案,其中包含有关使COM保持高兴的消息循环的重要性的更多详细信息。您可以在此处找到该帖子


3
很好的答案!我解决的错误是针对我为长期运行的报表构建器创建的线程而创建的,该线程使用WPF控件来构建报表的各个部分,因此这很有意义,尽管我不知道该线程上存在消息循环。 。
Benjol 2010年

3
我必须认真阅读MSDN帖子几次才能理解它,您的答案非常清楚且写得很好。谢谢!
ak3nat0n 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.