现在的位置: 首页 > 综合 > 正文

COM的Apartment概念

2013年01月06日 ⁄ 综合 ⁄ 共 3095字 ⁄ 字号 评论关闭

首先,COM的Apartment概念是为了让COM的开发和使用都容易才引入Apartment概念的。但是关于Apartment概念详细阐述的比较好的资料在国内可能比较少,所以有很多人对其理解上存在一些疑问。这很正常,我当初为理解它也花了2周时间,参考了不少资料。

其次,marshal的问题。marshal主要是COM用来在进程间以及计算机间进行COM调用时用的,即Proxy/stub模型。但是在进程内部有时也需要进行marshal,在下面详细阐述时,我会说明marshal相关的问题。

要注意的是,有一些COM的基本概念在这儿我不会说明,我想在这儿的讨论COM的人因该知道。还有,因为我用的是日语键盘,敲中文不方便,本文中有错别字还请多包涵。上面讲了不少废话,下面言归正传。

1。 COM Apartment的背景

大家都知道,在一个多线程的操作系统中,在线程中对一个多个线程公用的变量进行操作时,线程的同步是必须的。这个变量可以是一个简单的Integer类型,也可以是一个class或是一个COM对象。 对一个简单的Integer变量来说,线程的同步很简单,每次对它进行操作的时候用Mutex等进行同步。 对于一个class或COM对象来说,你也可以采用对简单变量一样的方法,但更好的方法是在其内部进行线程同步,这样便于使用。也就是说,你在实现这个class或COM对象时,就要写线程同步代码。如果class或COM对象内部实现了线程同步,那么它就是Thread-safe的。

现在的问题是,并不是每个人在写COM对象都保证它是Thread-safe的。 如果没有COM的Apartment, 那么我们对所有开发的COM对象都要贴上一个标签指明它是不是Thread-safe,这样使用这个COM对象的人才知道他如果要在多线程方式下使用这个COM对象时是不是要进行线程同步。 COM的Runtime为了使大家不用在自己开发的COM对象上贴上这么一个标签,而开发人员可以在没有写线程同步代码的情况下照样可以用不是Thread-safe的COM对象,引入了Apartment。

 

2。 COM Apartment的概念
        为了解决上面所讲的Thread-safe问题,引入了COM Apartment概念。但到底COM Apartment是什么? 大家考虑一下这个问题: 如果我写了一个不是Thread-safe的COM对象,把它交给一个使用者,而且告述他是Thread-safe的。那么如果使用者在多线程环境下用我写的这个COM对象就不会写线程同步代码,会出现什么情况? 答案是明显的,执行结果会有问题。 COM的Runtime为了使使用者在开发者没有告述他COM对象的Thread-safe问题的情况下也能在各个线程模式下安全使用,要求一个COM对象能够告述COM的Runtime环境它能在什么线程模式下被安全使用, 同时,使用者在使用一个COM对象之前,也必须告述COM的Runtime环境他将在什么线程模式下使用这个COM对象,如果两者的线程模式不一样,那么COM的Runtime环境就会介入,为它们完成线程的同步问题。COM Apartment就是COM的Runtime环境对COM Client和COM对象的线程模式的包装(实际上是在TLS里面加上了线程模式的标志)。 在开发一个COM对象时开发者必须指定这个COM对象的线程模式(这一点大家应该都已经知道了)。 COM对象的线程模式会在它注册时写入系统的注册表。 使用者在每个使用COM对象的线程中必须首先调用CoInitialize,CoInitializeEx等来告述COM Runtime环境将在那种线程模式下使用COM对象。

 

什么是套间(Apartments)
套间是COM为了简化对象对多线程的支持而推出的一套机制,用于指定线程和COM对象的多线程特性,并且对不同特性的套间之间的调用提供同步支持,保证不同多线程特性的对象之间可以互相正确调用而不会引入同步问题,简化编程(实际上可能搞得更复杂了)。比如,如果某个对象编写的时候忘记考虑多线程,或者没有时间考虑,或者没有必要提供实现多线程的支持,这个时候可以将对象指定为STA,让COM自动管理对该对象的调用,保证对象可以被正确调用,即使是多线程的调用也会被串行化(依次调用,而非同时调用)。反之,如果一个对象支持多线程调用,那么它可以被标记为MTA,COM会允许对其进行多线程的调用。
对于套间需要注意下面几点:
1.    套间并不是一个真实存在的一个区域,而是一个逻辑的概念
2.    套间表明了位于套间的代码的多线程特性,决定了以下几点
a.    代码本身允许单线程调用还是多线程调用
b.    代码创建COM对象拿到的是Proxy还是原始的指针(关于Proxy请参见后面的Proxy一节)
c.    代码调用同一套间的COM对象是通过原始指针,不同套间则通过Proxy
3.    线程必须属于某个套间,这表明了线程本身的多线程特性,也就是线程会对COM对象进行多线程的调用还是单线程的调用。比如,一个线程位于STA中,那么该线程只适合直接调用支持单线程调用的、同一套间的COM对象,其他COM对象则需要通过Proxy来间接调用,什么是Proxy后面会讲到。同时,线程所处的套间还决定了创建对象的时候所获得的对象是对象本身还是Proxy。线程在同一时间内只能属于一个套间或者不属于某个套间,但是线程可以在不同时间内属于不同的套间。典型的例子有,线程调用了CoInitialize,然后再调用CoUninitialize退出套间,之后又调用了CoInitialize进入了另外一个套间,此外,线程临时进入NTA也是一个例子。下面会讲到。
4.    COM对象也必须属于某个套间,同样的这决定了COM对象的多线程特性,和上面类似。COM对象不会从一个套间迁移到另外一个套间,如果这个套间被释放,那么这个对象也同时被释放,这个事实对于STA套间尤为重要。

 

可以把套间理解成线程的一种属性,或者说一个线程可以是一个套间线程,也可以不是。组件必须存活在拥有套间属性的线程中。使一个线程具备套间属性很简单,调用CoInitialize即可,或者OleInitialize/AfxOleInit等等COM初始化函数。初始化函数会为线程中容纳组件做一些必要的准备,例如创建一个隐藏窗口处理COM调用消息、代理存根管理器。

 

我觉得楼上两位有点搞笑,尝试用长篇大论的解说来解释清楚,书上也努力用长篇大论来解释,对于初学者来说,看长篇大论的书始终隔着一层膜,雾里看花,越长越糊涂。
楼主可能需要的是某种形象的比喻,用简洁的词汇点破这层膜,用比喻作为指导思想来逐步理解书上的每一句话。不过太可惜了,到目前为止还没有这种合适的比喻。最接近的比喻还是 Apartment 本身的字面意思(估计微软为取出这个名称还是很费了一番功夫的)——房子。

房子有几个特性能帮助理解(就不解释COM中的概念了):
1、组件这个东西不能晒太阳,所以东西都必须呆在房子里
2、房子只有一个门,想到房子拿东西必须从门进入
3、如果有多个人想同时进入房子,只能在门口排队,一个一个来
4、消息循环是房子的看门老头,平时门是关着的,进入房子的人得先敲门,如果看门老头在,它会开门放你进去,如果看门老头不在,你就干等吧
5、如果某些人使用下三滥的手段,比如砸墙、挖地道等等去偷东西,结果是什么?多个人都在抢一个东西,结果东西被撕烂了。

好了,套间就是一个房子,套间线程就是为线程建一个房子。话说回来,房子也不能解释全部。

抱歉!评论已关闭.