你能解释一下STA和MTA吗?

时间:2008-09-24 13:40:23

标签: .net multithreading com apartments

你能用自己的话解释STA和MTA吗?

此外,什么是公寓线程,它们只与COM有关?如果是这样,为什么?

8 个答案:

答案 0 :(得分:344)

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

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

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

答案 1 :(得分:196)

这完全取决于如何处理对象的调用,以及他们需要多少保护。 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中,基本上只在创建UI的任何线程上使用[STAThread]。工作线程应该使用MTA,除非他们要使用Apartment标记的COM组件,在这种情况下使用STA以避免编组开销和可伸缩性问题,如果从多个线程调用相同的组件(作为每个线程)将不得不依次等待组件。如果你为每个线程使用一个单独的COM对象,无论组件是在STA还是MTA中,它都会更容易。

答案 2 :(得分:71)

我发现现有的解释太gobbledygook。这是我用简单的英语解释的:

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

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

这几乎是它的要点。虽然从技术上讲,我没有提到一些细节,例如在'STA'段落中,创建者线程本身必须是STA。但是,了解STA / MTA / NA几乎需要了解这一点。

答案 3 :(得分:22)

STA(Single Threaded Apartment)基本上是一个概念,一次只有一个线程与您的代码交互。通过Windows消息(使用不可见的)窗口对您公寓的呼叫进行封送。这允许呼叫排队等待操作完成。

MTA(多线程公寓)是许多线程可以同时运行的地方,作为开发人员处理线程安全的责任在你身上。

要了解COM中的线程模型还有很多东西,但如果你无法理解它们是什么,那么我会说理解STA是什么以及它是如何工作的将是最好的起点,因为大多数COM对象是STA的。

Apartment Threads,如果一个线程与它正在使用的对象位于同一个公寓中,那么它就是一个公寓线程。我认为这只是一个COM概念,因为它只是谈论与它们交互的对象和线程的一种方式......

答案 4 :(得分:18)

每个承载COM或OLE控件的EXE都会定义它的公寓状态。公寓状态默认为STA(并且对于大多数程序应该是STA)。

STA - 所有OLE控件必须必须存在于STA中。 STA意味着必须始终在UI线程上操作COM对象,并且不能将其传递给其他线程(非常类似于MFC中的任何UI元素)。但是,您的程序仍然可以有很多线程。

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

答案 5 :(得分:11)

据我所知,'Apartment'用于保护COM对象免受多线程问题的影响。

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

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

答案 6 :(得分:4)

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

答案 7 :(得分:0)

旁注:如果您正在使用某些 PowerShell 2.0 管理单元,则需要使用 -MTA 选项启动 PowerShell 版本 3 或更高版本才能使用它们。 PowerShell 2 单元模型是 MTA,而更高版本默认使用 STA。另一点是位。公寓中的正常呼叫不会编组(直接呼叫),因此如果您的来电者是 x64,则被叫者也必须是 x64。解决这个问题的唯一方法是使用远程过程调用 (RPC),这会增加大量开销(产生一个新的 32 位进程以通过某种方式加载管理单元 DLL 和查询结果)。对于开发人员:始终发布类型库 -它使您的 COM 对象发现和使用更加容易!每个接口都应该是公开的和唯一的 - 实现可以是专有的或开源的。

另一种情况

示例:

 IStorage_vtbl** reference; // you got it by some means of factory
 
 
 public unsafe int OpenStorage(char* pwcsName, IStorage pstgPriority, uint grfMode, char** snbExclude, uint reserved, IStorage* ppstg)
 {
     IStorage_vtbl** @this = (IStorage_vtbl**)reference;
     IStorage_vtbl* vtbl = *@this;
     if (vtbl == null)
         throw new InvalidComObjectException();
     Delegate genericDelegate = Marshal.GetDelegateForFunctionPointer(vtbl->method_6, typeof(delegate_6));
     delegate_6 method = (delegate_6)genericDelegate;
     return method(@this, pwcsName, pstgPriority, grfMode, snbExclude, reserved, ppstg);
 }
 

这段代码只是添加了实例的'this'指针,以便真正调用COM子系统 那么,这个调用是打开 IStorage STA 实例还是 MTA 实例?