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

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


当前回答

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

If a COM object is not thread-safe, it should declare it as a STA object. Then only the thread who creates it can access it. The creation thread should declare itself as a STA thread. Under the hood, the thread stores the STA information in its TLS(Thread Local Storage). We call this behavior as that the thread enters a STA apartment. When other threads want to access this COM object, it should marshal the access to the creation thread. Basically, the creation thread uses messages mechanism to process the in-bound calls.

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

其他回答

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

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

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

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

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

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

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

Main thread (ThreadingModel value not present). The object is created on the host's main UI thread, and all calls are marshalled to that thread. The class factory will only be called on that thread. Apartment. This indicates that the class can run on any single-threaded-mode thread. If the thread that creates it is an STA thread, the object will run on that thread, otherwise it will be created in the main STA - if no main STA exists, an STA thread will be created for it. (This means MTA threads that create Apartment objects will be marshalling all calls to a different thread.) The class factory can be called concurrently by multiple STA threads so it must protect its internal data against this. Free. This indicates a class designed to run in the MTA. It will always load in the MTA, even if created by an STA thread, which again means the STA thread's calls will be marshalled. This is because a Free object is generally written with the expectation that it can block. Both. These classes are flexible and load in whichever apartment they're created from. They must be written to fit both sets of requirements, however: they must protect their internal state against concurrent calls, in case they're loaded in the MTA, but must not block, in case they're loaded in an STA.

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

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

If a COM object is not thread-safe, it should declare it as a STA object. Then only the thread who creates it can access it. The creation thread should declare itself as a STA thread. Under the hood, the thread stores the STA information in its TLS(Thread Local Storage). We call this behavior as that the thread enters a STA apartment. When other threads want to access this COM object, it should marshal the access to the creation thread. Basically, the creation thread uses messages mechanism to process the in-bound calls.

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

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

了解COM公寓,第一部分 理解COM公寓,第二部分

关于什么是Apartment的要点:

An apartment is a concurrency boundary; it’s an imaginary box drawn around objects and client threads that separates COM clients and COM objects that have incompatible threading characteristics. Every thread that uses COM, and every object that those threads create, is assigned to an apartment. When a thread calls COM’s CoInitialize or CoInitializeEx function, that thread is placed in an apartment. And when an object is created, it too is placed in an apartment. Whenever it creates a new apartment, COM allocates an apartment object on the heap and initializes it with important information such as the apartment ID and apartment type. When it assigns a thread to an apartment, COM records the address of the corresponding apartment object in thread-local storage (TLS).

Side note: If you are using some PowerShell 2.0 snap-ins, you need to launch PowerShell version 3 or greater with -MTA option to use them. PowerShell 2 apartment model is MTA versus later versions use STA as default. Other point is bitness. Normal calls in apartment are not marshalled (direct calls), so if your caller is x64 then callee must be also x64. Only way around this is to use remote procedure call (RPC), which add huge amount of overhead (spawn a new 32-bit process to load snap-in DLL and query result by some means).For developer: always publish type library - it make your COM object discovery and usage much easier! Every interface should be public and unique - implementation can be proprietary or open source.

另一种情况

例子:

 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的实例吗?

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

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

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