你能解释一下STA和MTA吗?

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

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

157800 次浏览

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

STA -所有OLE控件必须存在于STA中。STA意味着你的com对象必须总是在UI线程上操作,不能传递给其他线程(就像MFC中的任何UI元素一样)。但是,您的程序仍然可以有许多线程。

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

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

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

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

STA(单线程公寓)的基本概念是,一次只有一个线程与您的代码交互。打到你公寓的电话是通过窗口消息(使用一个不可见的)窗口编组的。这允许调用排队等待操作完成。

MTA(多线程公寓)是许多线程可以同时操作的地方,作为开发人员的您有责任处理线程安全性。

关于COM中的线程模型还有很多需要学习的地方,但是如果你在理解它们是什么方面有困难,那么我会说理解STA是什么以及它是如何工作的将是最好的起点,因为大多数COM对象都是STA的。

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

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

此外,如果从用户界面线程进行调用,运行时还需要防止COM对象调用阻塞用户界面。

公寓是存放对象的地方,它们包含一个或多个线程。公寓定义了打电话时发生的事情。对公寓中对象的调用将在该公寓中的任何线程上接收和处理,除非已经在正确公寓中的线程的调用由自身处理(即对对象的直接调用)。

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

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

相比之下,所有MTA线程共享一个进程的MTA。如果没有可用线程,COM可能会启动一个新的工作线程来处理传入的调用,直到池的限制。发出出站调用的线程只是阻塞。

为了简单起见,我们将只考虑在dll中实现的对象,这些对象通过为类的键设置ThreadingModel值,在注册表中通告它们支持什么。有四种选择:

  • 主线程(ThreadingModel值不存在)。对象是在主机的主UI线程上创建的,所有调用都被编组到该线程。类工厂将只在该线程上被调用。
  • Apartment。这表明该类可以在任何单线程模式的线程上运行。如果创建它的线程是一个STA线程,对象将在该线程上运行,否则它将在主STA中创建——如果不存在主STA,将为它创建一个STA线程。(这意味着创建Apartment对象的MTA线程将把所有调用编组到不同的线程。)类工厂可以被多个STA线程并发调用,因此它必须保护其内部数据不受此影响。
  • Free。这表示设计为在MTA中运行的类。它总是在MTA中加载,即使是由STA线程创建的,这也意味着STA线程的调用将被编组。这是因为Free对象通常在编写时期望它可以阻塞。
  • Both。这些类是灵活的,可以加载到创建它们的公寓中。然而,它们必须被编写来满足这两组需求:它们必须保护它们的内部状态不受并发调用的影响,以防它们加载在MTA中,但不能阻塞,以防它们加载在STA中。

在. net框架中,基本上只要在创建UI的任何线程上使用[STAThread]即可。工作线程应该使用MTA,除非它们将使用__abc1标记的COM组件,在这种情况下,如果从多个线程调用相同的组件,则使用STA来避免编组开销和可伸缩性问题(因为每个线程都必须依次等待组件)。如果每个线程使用一个单独的COM对象,无论组件是在STA还是MTA中,都要容易得多。

我发现现有的解释太官样文章了。下面是我的解释:

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

这差不多就是要点了。虽然从技术上讲,还有一些细节我没有提到,比如在“STA”段落中,创造者线程本身必须是STA。但这几乎是你了解STA/MTA/NA所需要知道的全部内容。

根据我的理解,“Apartment”是用来保护COM对象不受多线程问题的影响。

如果COM对象不是线程安全的,它应该将其声明为STA对象。那么只有创建它的线程才能访问它。创建线程应该将自己声明为STA线程。在底层,线程将STA信息存储在其TLS(线程本地存储)中。我们将此行为称为线程进入STA单元。当其他线程想要访问这个COM对象时,它应该编组对创建线程的访问。基本上,创建线程使用消息机制来处理入站调用。

如果一个COM对象是线程安全的,它应该声明为一个MTA对象。MTA对象可以被多线程访问。

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

附注:如果您正在使用PowerShell 2.0管理单元,您需要启动PowerShell版本3或更高版本,并带有-MTA选项才能使用它们。PowerShell 2的公寓模式是MTA,而后来的版本默认使用STA。另一点是咬性。公寓中的正常呼叫(直接呼叫)是不编组的,所以如果您的caller是x64,那么callee也必须是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);
}
 

这段代码只是为COM子系统的实际调用添加了实例的“This”指针 那么,这个调用是打开IStorage STA或MTA的实例吗?< / p >

本文解释了STA &MTA非常清楚。

< p > 了解COM公寓,第一部分
理解COM公寓,第二部分 < / p >

关于什么是Apartment的要点:

  • 公寓是一个并发边界;它是一个围绕对象和客户端线程绘制的假想框,用来分隔具有不兼容线程特性的COM客户端和COM对象。
  • 使用COM的每个线程以及这些线程创建的每个对象都分配给一个单元。
  • 当一个线程调用COM的CoInitializeCoInitializeEx函数时,该线程被放置在一个单元中。当一个物体被创造出来时,它也被放置在一个公寓里。
  • 每当创建一个新公寓时,COM都会在堆上分配一个公寓对象,并使用公寓ID和公寓类型等重要信息初始化它。当它将一个线程分配给一个公寓时,COM将对应公寓对象的地址记录在线程本地存储(TLS)中。