前言
昨天做CLR上的Winform开发,不小心将一个函数放到了[STAThreadAttribute]和int main(array<System::String ^> ^args)的中间,然后就悲剧了,总是报错,郁闷,排查了一个遍,才发现这个问题,所以我就查了一下[STAThreadAttribute]到底是个什么东东!
一、含义
[STAThread]
STAThread:Single Thread Apartment Thread.(单一线程单元线程)
[]是用来表示Attributes;
二、作用
[STAThread]
是一种线程模型,用在程序的入口方法上(在C#和VB.NET里是Main()方法),来指定当前线程的
ApartmentState是STA。用在其他方法上不产生影响。
这个属性只在 Com Interop 有用,如果全部是 managed code 则无用。
简单的说法:[STAThread]指示应用程序的默认线程模型是单线程单元(STA)。
启动线程模型可设置为单线程单元或多线程单元。
如果未对其进行设置,则该线程不被初始化。也就是说如果你用的.NETFramework,并且没有使用COM
Interop(一种服务,它使 .NET Framework 对象能够与 COM 对象通信),一般不需要这个Attribute。其它的还有MTA(多线程套间)、Free Thread(自由线程)。
[STAThread] attribute指示应用程序的 COM 线程模型是单线程单元。
而于此对应的多线程单元则是 [MTAThread] (多线程单元线程)
COM 线程模型只适用于使用 COM interop 的应用程序。如果将此属性应用到不使用 COM interop 的应用程序,将没有任何效果。
COM 线程模型可设置为单线程单元或多线程单元。如果应用程序线程实际调用了 COM 组件,则仅为 COM
interop 初始化该线程。如果没有使用 COM interop,则不初始化该线程。
三、Com组件(wiki)
COM实质上是一种语言无关的对象的实现方式,使其可以在创建环境不同的场合被复用,甚至是跨计算机的分布环境。COM允许复用这些对象而不必知道对象内部是如何实现的,因为组件实现者必须提供良好定义的界面从而屏蔽了实现细节。通过引用计数,组建对象自己负责动态创建与销毁,从而屏蔽了不同编程语言的内存分配语义的差异。在COM界面(interface)之间的类型转换,通过QueryInterface方法。
对于某些应用程序,COM已经部分被Microsoft .NET框架取代。COM通过Windows Communication Foundation (WCF)支持Web Service。COM对象通过.NET COM Interop可以被所有.NET语言使用。网络化的DCOM使用二进制私有格式,而WCF鼓励使用基于XML的SOAP消息机制。COM非常类似于其他软件组件界面技术,如CORBA与JavaBeans,各自有其优点与弱点。
与C++不同,COM提供了一个稳定的应用二进制接口(ABI),不随编译器版本而变。
四、单线程单元与多线程单元(wiki)
在COM中,线程通过apartment寻址。所有COM对象存在于一个apartment,既可以是单线程的也可以是多线程的。COM中有3类apartment:单线程Apartment (STA),多线程Apartment (MTA),以及Thread Neutral Apartment(NA)。每一种apartment表示对象内部如何在多线程下同步的机制。一个进程可以包含多个COM对象,其中一些可以用STA,其它可以用MTA。所有线程以类似方式访问同一apartment中的COM对象。COM对象与线程的apartment的选择在运行时确定后就不能再改变。
Apartment类型 描述 单线程Apartment[11](STA),(ThreadingModel=Apartment) 单线程用于执行对象的方法。apartment之外的线程调用的方法要marshalling并由操作系统自动排队(通过一个标准的Windows消息队列)。因此COM运行时提供了自动同步以确保对象的方法调用执行完毕后才能启动新的方法调用。开发者不需要担心线程上锁(locking)或竞态条件。 多线程Apartment[12](MTA),(ThreadingModel=Free) COM运行时不提供同步,多个线程可以同时调用COM对象。COM对象因而需要执行自己的同步控制以避免多线程同时访问造成的竞态条件。STA状态的线程调用MTA状态的对象也需要marshalling。 Thread Neutral Apartment(NA),(ThreadingModel=Neutral) 一个特殊的apartment,没有任何指定的线程。当STA或MTA线程调用同一进程的NA对象,则主调线程临时离开它的apartment并执行对象的代码,没有任何线程切换。[13]因此NA可以认为是优化apartment之间方法调用的效率。 线程与属于同一apartment的对象遵循相同的线程访问规则。可以直接执行方法调用而不需COM的辅助。跨apartment的方法调用需要marshalling。这需要代理(proxies)与“桩”(stubs)。
除了以上模式,COM服务器也可以指定双apartment模式。即在对象创建时根据调用线程的apartment类型自动选择STA或MTA。[14]这避免了STA线程访问MTA服务器的marshaling开销。