你能解释一下STA和MTA吗?

Posted

技术标签:

【中文标题】你能解释一下STA和MTA吗?【英文标题】:Could you explain STA and MTA? 【发布时间】:2010-09-12 17:37:47 【问题描述】:

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

另外,什么是单元线程,它们是否只与 COM 相关?如果有,为什么?

【问题讨论】:

另见:了解 COM 公寓 Part 1, Part 2 STA 允许使用不支持线程同步的工具(如 VB6)开发的 COM 组件在多线程应用程序中使用。 【参考方案1】:

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

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

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

【讨论】:

详细阅读:INFO: Descriptions and Workings of OLE Threading Models.【参考方案2】:

这完全取决于如何处理对对象的调用,以及它们需要多少保护。 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 组件,在这种情况下,如果从多个线程调用相同的组件(因为每个线程将有依次等待组件)。如果您对每个线程使用单独的 COM 对象,无论组件是在 STA 还是 MTA 中,这一切都会变得容易得多。

【讨论】:

我喜欢你的最后一个结论,但关于这一点,如果我想在我的 UI 上添加一个 UserControl,而唯一能做的就是重现一个 gif(如加载器),我应该怎么做。 . 我遇到了麻烦,如果它们在同一个线程中,gif 不会旋转......我不确定 UI 上的 MTA 是否是个好主意,你会怎么做? @Yogurtu:你为什么要担心 COM 线程模型?只有在代码中使用 COM 对象时,STA/MTA 决策才有意义。您不能将 MTA 用于 UI - .NET 的内部不打算以这种方式使用。如果您的动画停止,那是因为您停止在 UI 线程上发送消息。将长时间运行的操作移至 BackgroundWorker 或拆分为小步骤。工作需要 “公寓”和appdomain有什么区别?【参考方案3】:

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

STA: 如果一个线程创建了一个设置为 STA 的 COM 对象(在调用 CoCreateXXX 时,您可以传递一个将 COM 对象设置为 STA 模式的标志),那么只有这个线程可以访问这个 COM 对象(这就是 STA 的意思——单线程单元),试图调用此 COM 对象上的方法的其他线程在后台默默地转为将消息传递给创建(拥有)COM 对象的线程。这很像只有创建 UI 控件的线程才能直接访问它。而且这种机制是为了防止复杂的锁定/解锁操作。

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

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

【讨论】:

【参考方案4】:

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

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

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

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

【讨论】:

【参考方案5】:

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

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

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

【讨论】:

“STA 意味着您的 COM 对象必须始终在 UI 线程上进行操作”我认为这并不完全正确......它不必在“UI”线程上,只有一个 STA 线程上有一个消息泵,因为调用同步使用消息。 UI 线程通常会满足这些要求,但这不是唯一的可能性。【参考方案6】:

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

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

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

【讨论】:

【参考方案7】:

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

【讨论】:

【参考方案8】:

旁注:如果您使用某些 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 的实例?

【讨论】:

【参考方案9】:

这篇文章非常清楚地解释了 STA 和 MTA。

Understanding COM Apartments, Part IUnderstanding COM Apartments, Part II

关于什么是公寓的要点:

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

【讨论】:

以上是关于你能解释一下STA和MTA吗?的主要内容,如果未能解决你的问题,请参考以下文章

你能解释一下输出并指出错误吗

你能解释一下提供的例子中的分类报告(召回率和精度)吗?

你能解释一下 lambda 表达式吗? [复制]

你能解释一下为啥 DirectoryInfo.GetFiles 会产生这个 IOException 吗?

你能解释一下 mod_wsgi 和 werkzeug 之间更详细的区别吗? (求救新手)

你能解释一下这个 Go 指针操作的行为吗?