.net 傻瓜的公寓状态
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4154429/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ApartmentState for dummies
提问by Benjol
I just corrected a bug using this:
我刚刚使用这个修正了一个错误:
_Thread.SetApartmentState(ApartmentState.STA);
Now I'd like to understand what it means, and why it works!
现在我想了解它的含义,以及它为什么起作用!
回答by Hans Passant
COM is the grand father of .NET. They had pretty lofty goals with it, one of the things that COM does but .NET completely skips is providing threading guarantees for a class. A COM class can publish what kind of threading requirements it has. And the COM infrastructure makes sure those requirements are met.
COM 是 .NET 的祖父。他们有相当崇高的目标,COM 所做但 .NET 完全跳过的一件事是为类提供线程保证。COM 类可以发布它具有什么样的线程需求。COM 基础结构确保满足这些要求。
This is completely absent in .NET. You can use a Queue<> object for example in multiple threads but if you don't lock properly, you'll have a nasty bug in your code that is very hard to diagnose.
这在 .NET 中完全没有。例如,您可以在多个线程中使用 Queue<> 对象,但是如果您没有正确锁定,您的代码中就会出现一个很难诊断的严重错误。
The exact details of COM threading are too large to fit in a post. I'll focus on the specifics of your question. A thread that creates COM objects has to tell COM what kind of support it wants to give to COM classes that have restricted threading options. The vast majority of those classes only support so-called Apartment threading, their interface methods can only safely be called from the same thread that created the instance. In other words, they announce "I don't support threading whatsoever, please take care of nevercalling me from the wrong thread". Even if the client code actually doescall it from another thread.
COM 线程的确切细节太大,无法在帖子中列出。我将重点关注您问题的具体细节。创建 COM 对象的线程必须告诉 COM 它想要为具有受限线程选项的 COM 类提供什么样的支持。绝大多数这些类只支持所谓的单元线程,它们的接口方法只能从创建实例的同一个线程中安全地调用。换句话说,他们宣布“我不支持任何线程,请注意不要从错误的线程中调用我”。即使客户端代码实际上不会从另一个线程调用它。
There are two kinds, STA (Single Threaded Apartment) and MTA. It is specified in the CoInitializeEx() call, a function that must be called by any thread that does anything with COM. The CLR makes that call automatically whenever it starts a thread. For the main startup thread of your program, it gets the value to pass from the [STAThread] or [MTAThread] attribute on your Main() method. Default is MTA. For threads that you create yourself it is determined by your call to SetApartmentState(). Default is MTA. Threadpool threads are always MTA, that cannot be changed.
有两种,STA(单线程单元)和MTA。它在 CoInitializeEx() 调用中指定,该函数必须由任何对 COM 执行任何操作的线程调用。每当启动线程时,CLR 都会自动进行该调用。对于程序的主启动线程,它从 Main() 方法的 [STAThread] 或 [MTAThread] 属性获取要传递的值。默认为 MTA。对于您自己创建的线程,它由您对 SetApartmentState() 的调用决定。默认为 MTA。线程池线程始终是 MTA,无法更改。
There's lots of code in Windows that requires an STA. Notable examples you'd use yourself are the Clipboard, Drag + Drop and the shell dialogs (like OpenFileDialog). And a lot of code that you cannot see, like UI Automation programs and hooks to observe messages. None of that code has to be thread-safe, its author would have a very difficult time making it safe without knowing in which program it gets used. Accordingly the UI thread of a WPF or Windows Forms project must always be STA to support such code, as does any thread that creates a window.
Windows 中有很多代码需要 STA。您自己使用的值得注意的示例是剪贴板、拖放和外壳对话框(如 OpenFileDialog)。还有很多你看不到的代码,比如 UI 自动化程序和观察消息的钩子。没有一个代码必须是线程安全的,如果不知道它在哪个程序中使用,它的作者将很难使其安全。因此,WPF 或 Windows 窗体项目的 UI 线程必须始终是 STA 以支持此类代码,就像创建窗口的任何线程一样。
The promise you make to COM that your thread is STA however doesrequire you to follow the single-thread apartment contract. They are pretty stiff and you can get tricky to diagnose trouble when you break the contract. Requirements are that you never block the thread for any amount of time and that you pump a message loop. The latter requirement is met by a WPF or Winforms' UI thread but you will need to take care of it yourself if you create your own STA thread. The common diagnostic for breaking the contract is deadlock.
您向 COM 承诺您的线程是 STA 但是确实要求您遵循单线程单元合同。它们非常僵硬,当您违反合同时,您可能很难诊断出问题。要求是您永远不要在任何时间内阻塞线程并且您泵送消息循环。后一个要求由 WPF 或 Winforms 的 UI 线程满足,但如果您创建自己的 STA 线程,则需要自己处理。破坏合同的常见诊断方法是死锁。
There's quite a bit of support built-in the CLR to support these requirements btw, helping you to keep out of trouble. The lockstatement and WaitOne() methods pump a message loop when it blocks on an STA thread. This however only takes care of the never-block requirement, you still need to create your own message loop. Application.Run() in both WPF and Winforms.
顺便说一句,CLR 内置了相当多的支持来支持这些要求,帮助您避免遇到麻烦。该锁声明和WaitOne的()方法泵消息循环,当它在一个STA线程块。然而,这仅满足永不阻塞的要求,您仍然需要创建自己的消息循环。WPF 和 Winforms 中的 Application.Run()。
I've previously contributed an answer that contains more details about the significance of having a message loop to keep COM happy. You'll find the post here.

