您能解释一下STA和MTA吗?


Answers:


361

COM线程模型称为“公寓”模型,其中已初始化COM对象的执行上下文与单个线程(单线程单元)或多个线程(多线程单元)相关联。在此模型中,COM对象一旦在单元中初始化,就在其运行期间成为该单元的一部分。

STA模型用于不是线程安全的COM对象。这意味着他们不处理自己的同步。这是UI组件的常见用法。因此,如果另一个线程需要与该对象进行交互(例如按表单中的按钮),则该消息将被编组到STA线程上。Windows窗体消息泵送系统就是一个例子。

如果COM对象可以处理自己的同步,则可以使用MTA模型,其中允许多个线程与对象进行交互而无需编组调用。



208

这完全取决于如何处理对对象的调用以及它们需要多少保护。COM对象可以要求运行时保护它们,以防止多个线程同时调用它们。那些可能无法从不同线程并发调用的线程,因此它们必须保护自己的数据。

另外,如果从用户界面线程进行了调用,则运行时还必须防止COM对象调用阻塞用户界面。

一个公寓是对象住的地方,它们包含一个或多个线程。公寓定义了拨打电话时发生的情况。将在该公寓中的任何线程上接收并处理对公寓中对象的调用,但由本身已处理正确的公寓中的线程进行的调用(即,直接调用该对象)除外。

线程可以位于单线程单元中(在这种情况下,它们是该单元中的唯一线程),也可以位于多线程单元中。它们指定线程在何时初始化该线程的COM。

STA主要是为了与绑定到特定线程的用户界面兼容。STA通过接收到隐藏窗口的窗口消息来接收处理呼叫的通知;当它进行出站呼叫时,它将启动一个模式消息循环,以防止其他窗口消息被处理。您可以指定要调用的消息过滤器,以便您的应用程序可以响应其他消息。

相比之下,所有MTA线程都为该进程共享一个MTA。如果没有可用的线程(上限为池限制),COM可能会启动一个新的工作线程来处理传入的调用。进行出站呼叫的线程只会阻塞。

为简单起见,我们将仅考虑在DLL中实现的对象,这些对象通过设置ThreadingModel类的键的值在注册表中公布其支持的内容。有四个选项:

  • 主线程(ThreadingModel值不存在)。该对象是在主机的主UI线程上创建的,所有调用均被编组到该线程。类工厂只会在该线程上被调用。
  • Apartment。这表明该类可以在任何单线程模式线程上运行。如果创建它的线程是STA线程,则该对象将在该线程上运行,否则它将在主STA中创建-如果不存在主STA,则将为其创建STA线程。(这意味着创建Apartment对象的MTA线程将整理对另一个线程的所有调用。)类工厂可以由多个STA线程并发调用,因此它必须防止其内部数据受到攻击。
  • Free。这表示一个旨在在MTA中运行的类。即使由STA线程创建,它将始终加载到MTA中,这再次意味着STA线程的调用将被整理。这是因为Free通常在编写对象时会期望它会阻塞。
  • Both。这些类很灵活,可以在创建它们的任何单元中加载。但是,必须编写它们以同时满足这两组要求:必须将其内部状态防止并发调用(如果它们已装入MTA中),但不能阻塞(如果它们已装入STA中)。

从.NET Framework开始,基本上只[STAThread]在创建UI的任何线程上使用。辅助线程应使用MTA,除非它们将使用Apartment带有标记的COM组件,在这种情况下,如果从多个线程中调用同一组件,请使用STA避免编组开销和可伸缩性问题(因为每个线程都必须等待组件)。如果每个线程使用一个单独的COM对象(无论该组件位于STA还是MTA中),这将更加容易。


我喜欢您的最后结论,但是关于这一点,如果我想在UI上添加一个UserControl,该控件唯一要做的就是复制gif(如加载程序),该怎么办...我对此很麻烦,如果它们位于同一线程中,gif不会旋转...而且我不确定UI上的MTA是否是个好主意,您会怎么做?
Yogurtu

2
@Yogurtu:您为什么完全担心COM线程模型?仅当您在代码中使用COM对象时,STA / MTA决策才有意义。您不能将MTA用于UI-不打算以这种方式使用.NET的内部。如果动画停止,那是因为您停止在UI线程上泵送消息。将长时间运行的操作移至BackgroundWorker或分成几个小步骤。工作需要<16ms才能保持流畅的60Hz动画!
Mike Dimmick

“公寓”和appdomain有什么区别?
Puchacz

78

我发现现有的解释也太糟糕了。这是我用普通英语的解释:

STA:如果线程创建一个设置为STA的COM对象(调用CoCreateXXX时,您可以传递一个将COM对象设置为STA模式的标志),那么只有该线程可以访问此COM对象(这就是STA的含义-单线程公寓),试图在此COM对象上调用方法的其他线程在后台被悄悄转变为向创建(拥有)COM对象的线程传递消息。这非常类似于只有创建UI控件的线程才能直接访问它的事实。并且该机制旨在防止复杂的锁定/解锁操作。

MTA:如果线程创建了一个设置为MTA的COM对象,那么几乎每个线程都可以直接在其上调用方法。

这几乎是要点。尽管从技术上讲我没有提到一些细节,例如在“ STA”段落中,但是创建者线程本身必须是STA。但是,这几乎是您了解STA / MTA / NA所需要知道的全部。


23

STA(单线程单元)基本上是一个概念,一次只能有一个线程与您的代码进行交互。通过Windows消息(使用不可见的窗口)将打进公寓的电话编组起来。这样可以将呼叫排队,并等待操作完成。

MTA(多线程单元)是许多线程可以同时运行的地方,开发人员有责任处理线程安全。

关于COM中的线程模型,还有很多要学习的知识,但是如果您在理解它们的含义时遇到困难,那么我会说了解STA是什么以及它如何工作将是最好的起点,因为大多数COM对象都是STA的。

公寓线程,如果线程与其所使用的对象位于同一公寓中,则它是公寓线程。我认为这只是一个COM概念,因为它只是谈论与之交互的对象和线程的一种方式。


19

承载COM或OLE控件的每个EXE都定义其驻留状态。默认情况下,单元状态为STA(对于大多数程序,应为STA)。

STA-根据需要,所有OLE控件都必须位于STA中。STA意味着您的COM对象必须始终在UI线程上进行操作,并且不能传递给其他线程(与MFC中的任何UI元素一样)。但是,您的程序仍然可以有许多线程。

MTA-您可以在程序中的任何线程上操作COM对象。


13
“ STA意味着必须始终在UI线程上操纵您的COM对象”我认为这不是完全正确的……它不必在“ UI”线程上,只有具有消息泵,因为调用使用消息进行了同步。UI线程通常会满足这些要求,但这不是唯一的可能性。
布赖恩·奥尼尔

12

据我了解,“公寓”用于保护COM对象免受多线程问题的影响。

如果COM对象不是线程安全的,则应将其声明为STA对象。然后,只有创建它的线程才能访问它。创建线程应将自己声明为STA线程。在后台,线程将STA信息存储在其TLS(线程本地存储)中。我们将此行为称为线程进入STA公寓。当其他线程想要访问此COM对象时,它应封送对创建线程的访问。基本上,创建线程使用消息机制来处理入站调用。

如果COM对象是线程安全的,则应将其声明为MTA对象。可以通过多线程访问MTA对象。


4

调用COM对象dll(例如,读取专有数据文件)的代码在用户界面上可能工作正常,但从服务中神秘地挂起。原因是从.Net 2.0开始,用户界面采用STA(线程安全),而服务采用MTA((在此之前,服务采用STA)。必须为服务中的每个COM调用创建STA线程会增加大量开销。

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.