C# 应用程序进程中的 COM 线程单元(STA、MTA)管理

COM thread apartments (STA, MTA) management in C# app process

我正在尝试了解 C# 线程单元并有疑问:

  1. 公寓到底是什么,里面有什么?

  2. The Apartment and the COM Threading Architecture

    A process can have zero or more single-threaded apartments and zero or one multithreaded apartment.

    谁能提供应用程序的 C# 代码示例或解释,其中:

    1. 0 个 STA,0 个 MTA
    2. 1 个 STA,0 个 MTA
    3. 2 个 STA,0 个 MTA
    4. 0 个 STA,1 个 MTA
    5. 1 个 STA,1 个 MTA
    6. 2 个 STA,1 个 MTA

以及什么时候每个案例should\can被使用?

  1. COM 单元是一个逻辑概念。套间包含线程。一个 STA 单元只能包含一个线程(因此得名 "single threaded apartment")。一个 MTA 可以包含许多线程(因此得名 "multi threaded apartment")。由于一个 MTA 可以包含任意多个线程,因此一个进程中最多有一个 MTA。

  2. 您可以轻松地自己编写这些示例。只需在C#中创建线程,并在线程入口点方法的开头,调用Thread.SetApartmentState。您设置为 MTA 的任何线程都将存在于单个 MTA 中;您设置为 STA 的任何线程都将存在于自己的 STA 中。

不要尝试 fiddle 线程池线程的单元状态。这些线程是共享的(它是一个池),因此尝试改变它们的工作方式并不好。另外,线程一旦属于一个套间,就无法更改。

哦,我差点忘了回答"when each case can / should be used":从来没有,如果你能帮助的话。

即使在编写 COM 对象时,我也总是更喜欢编写 "free-threaded" 对象(不在乎它们在哪个公寓),但我可以做到,因为我有超强的理解力线程并了解如何使用互斥锁。线程模型的创建是因为(或者至少部分是因为)许多早期的半专业 VB 开发人员不理解这些概念(也许仍然不理解?),因此 COM 人员试图想出一个系统那仍然会让他们轻松地做一些多线程的事情。

因此,当您尝试将 COM 对象从一间公寓传递到另一间公寓时,会发生什么情况?甚至从一间公寓打电话到另一间公寓也必须通过特殊(恼人的)魔法。这就是所有关于代理和存根的爵士乐的用武之地。如果您想了解所有这些东西是如何工作的,那么……您知道文档在哪里。但是,如果您出于某种原因不必使用它(例如与现有 COM 对象的互操作),请尽可能避免使用它。

来自评论的其他问题:

we can set apartment for thread, using Thread.SetApartmentState for a thread or [STA\MTAThread] attribute for method. But can we get\set apartment for objects?

没有。一个 COM 对象属于它创建时所在的公寓,最后。

all calls to object which was created in STA apartment by its thread should be performed by this thread.

对于属于 STA 的对象:是和否。 "No" 是因为由于我之前提到的特殊(烦人的)魔法(代理和存根),您可以在 STA#2 的不同线程上获得对属于 STA#1 的 COM 对象的引用,并从那里打电话。 "Yes" 是因为幕后发生的事情是 proxy/stub 魔法从一个线程向另一个线程发送消息,并且该方法实际上总是在对象所属的线程上执行。因此,您可以从任何线程/启动/跨单元调用...但方法本身将在对象所属的线程上执行。经常出现的一个问题是,出于某种原因,proxy/stub 魔法对给定的 COM 对象不可用……在这种情况下,您会陷入困境;你真的只能直接在它所在的线程上使用那个对象。

对于 MTA,它稍微宽松一些——特殊的(烦人的)封送处理仅在跨越 apartment 边界时才会发挥作用,而不是线程边界。因此,如果您的 MTA 中有多个线程,您必须小心处理自己的同步。