前些时间转载了一篇关于COM线程模型的文章,感觉效果还是很一般,我后来又搜集了些资料,打算自己写一篇非常通俗易懂,而又准确的文章,这就是本文。
泛说"COM线程模型":http://blog.csdn.net/guogangj/archive/2007/09/06/1774280.aspx
Apartment,中文翻译为“公寓”,有时候为“套间”,这里就翻译为“公寓”吧,都一个意思,指的就是COM的线程模式,这个概念很抽象,理解起来比较困难。因为公寓不像Windows内核对象那样有个“句柄”,并且跟公寓相关的Windows API很少,只有5个:CoInitialize,CoUninitialize,CoInitializeEx,OleInitialize和OleUninitialize,大家都很熟悉了,5个关于COM初始化和反初始化的函数。如何来理解公寓呢?可以这样:1、线程住在公寓中;2、对象住在公寓中。有时候,对象和创建它的线程住在同一个公寓中,有时候不是。这样还是很难理解对吧,但没事,这个先记下来,后面会明白的。
COM只有两种公寓,一种叫单线程公寓(Single-Thread Apartment),简称STA,一种叫多线程公寓(Multi-Thread Apartment),简称MTA,顾名思义,一种只能容纳一个线程,另一种能容纳多个线程。在一个进程中,MTA只有一个,而STA可以有很多。
我们在使用COM之前,都应该先初始化COM,怎么初始化?当然是前文提到的那几个函数了,CoInitialize,CoInitializeEx和OleInitialize,那我们是每个程序(进程)初始化一次还是每个线程初始化一次?答案是线程,每个线程初始化一次,这么个初始化,就相当于把这个线程安置在某个公寓中。具体这样的:CoInitialize或OleInitialize把线程放置入STA;CoInitializeEx允许你把线程放置入MTA。从公寓中移除线程的方法是CoUninitialize和OleUninitialize。
我们都知道,对象是线程创建的,那对象什么时候跟创建它的线程同一个公寓,什么时候不是同一个公寓呢?前面说了线程所在的公寓类型是由那几个初始化函数所决定,那对象所在什么公寓是由什么决定的呢?这个稍微复杂一点,答案是:由创建它的线程的类型及对象本身的线程属性所决定。线程类型大家都知道啦,就前面提到的由那几个初始化函数决定,那么对象本身线程属性怎么来定呢?答案:注册表里的信息来定。
打开注册表编辑器,按照这路径:“/HKEY_CLASSES_ROOT/CLSID/{00000010-0000-0010-8000-00AA006D2EA4}/InprocServer32”,(这个GUID很奇怪吧,明显不是用工具生成的,微软可有手动填写GUID的特权哦)看看“ThreadingModel”的值,嗯,没错,是“Apartment”,这个“Apartment”就是刚才我所提到的“对象本身线程属性”,对象本身线程属性一共有四种:Apartment、Both、Free和Single。下面我列个表,一目了然。
组件线程属性 | STA线程 | MTA线程
--------------------------------------------------------------------------------------
Apartment | 同一公寓中,直接访问 | 创建一个STA,用代理访问
Free | MTA中,用代理访问 | MTA,直接访问
Both | 同一公寓中,直接访问 | MTA,直接访问
Single | 主STA中,通过主STA访问
备注:第一个以COINIT_APARTMENTTHREADED调用CoInitializeEx()的线程被称作是主STA。
例如一个STA线程创建了一个本身线程属性为Free的对象,那该对象存在于MTA中,这个STA线程访问它就得通过代理,当然了,这对程序员来说是透明的,因为这个功能是靠COM的remoting层来实现的。要说和直接访问有什么能体现出来的不同,可能就是通过代理访问会慢一些,毕竟消息需要Marshalling,但这几毫秒的时间差你们地球人是很难感觉出来的(J)。
那么很明显了,只要我们把组件类型设置为Apartment,就不会有任何线程访问冲突的问题。