COM原理之套间

1.    COM套间的由来

为了解决COM组件在多线程环境下临界资源的访问。

2.    COM套间的类型

1)    客户端程序创建的COM线程模型

a)     单线程套间(STA)

单线程套间可以保证,调用COM对象的方法被顺序执行,COM库所做的幕后工作是在我们调用CoInitializeEx(NULL,COINIT_APARTMENTTHREADED)的时候,会生成一个隐藏窗口,对COM对象所有的调用,都会发消息到线程的消息队列,最终到隐藏窗口的窗口过程调用COM对象的相关方法,由于消息队列是线性执行的,所以保证了COM对象的调用为串行调用。

CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);

上述代码使用COM库函数创建了一个STA,并把当前线程放入STA中,一个STA只关联一个线程,只有这个线程才能直接调用它创建的COM对象。

如果其他线程需要使用该对象,可以通过以下两种方式:

l  列集和散集CoMarshalInterThreadInterfaceInStreamCoGetInterfaceAndReleaseStream。

线程A调用CoMarshalInterThreadInterfaceInStream列集接口指针,线程B调用CoGetInterfaceAndReleaseStream进行散集。通过函数CoGetInterfaceAndReleaseStream,COM在调用者套间中创建新的代理来访问其他线程的COM对象。如果接口指针不需要进行列集(例如,两个线程共享同一个套间的情况(MTA)),CoGetInterfaceAndReleaseStream会智能地不创建代理,直接调用该对象。

l  Global Interface Table,GIT

GIT是每个进程一个的全局表格,让各个线程可以安全地共享接口指针。如果线程A想要与同一个进程中的其他线程共享接口指针,可以使用IGlobalInterfaceTable::RegisterInterfaceInGlobal将接口指针放到GIT中。线程B可以调用IGlobalInterfaceTable::GetInterfaceFromGlobal来获取接口指针。线程B从GIT获取接口指针的时候,接口指针会被列集到获取线程B所属的套间中。

综上,如果多个线程间访问同一COM对象,建议调用列集/散集或GIT函数,因为COM库只会在有必要的时候才进行列集/散集操作。

b)    多线程套间(MTA)

CoInitializeEx(NULL,COINIT_MULTITHREADED);

一个进程只会存在一个MTA,它可以关联多个线程。第一次调用的时候,会创建MTA,然后当前线程和MTA关联在一起,线程被标记为自由线程。以后线程再调用该函数(同一进程)的时候,线程直接放到之前的MTA中,并标记为自由线程。

COM库不会保证组件调用的串行性,多线程调用组件时,需要组件开发者保证组件本身的线程安全性。

2)    COM组件的线程模型

Single(单线程)、Apartment、Free、 Both。

l  Single:所有对Single对象的调用都会发送到主线程套间,也就是第一个创建Single对象的线程套间。(绝对的线程安全,不需要组件开发者保证)

l  Apartment:对象的调用会分别在创建此COM对象的线程套间执行,对象是在哪个线程套间创建的,所有针对其对象的调用都会汇集到创建Com对象的线程套间,简单来说Single就是多个调用汇集到第一个创建对象的线程套间(只有一个),Apartment是多个调用汇集到各自创建者的线程套间(可以多个)。(相对线程安全,当多线程访问全局变量,静态变量时需要lock)

l  Free:并发执行,非线程安全,需要组件开发者自己保证线程安全。

l  Both:对象既可以运行在STA中,也可以运行在MTA中。既然他可以运行在MTA中,那么他就是非线程安全,需要组件开发者自己保证线程安全。

为什么需要Both类型呢?假设,我们在组件A中调用另一个组件B,如果A所在套间是一个STA,而调用的组件是Free类型的,这样就需要为组件B创建一个MTA的线程套间,这样这两个对象就必须存在于不同的套间中。而跨套间的调用,需要通过中间代理来实现,这样必然会损失性能。如果是Both的话,则避免了这种开销。

3)    客户端程序的线程套间模型和组件线程模型关系

l  若STA创建Single/Apartment型组件,是直接创建,直接调用。

l  若STA创建Free型组件,COM库为组件创建一个MTA,通过代理访问组件。

l  若STA创建Both型组件,是直接创建,直接调用。

l  若MTA创建Single/Apartment型组件,COM库为组件创建一个STA,通过代理访问组件。

l  若MTA创建Free型组件,是直接创建,直接调用。

l  若MTA创建Both型组件,是直接创建,直接调用。

所以,如果客户端程序的线程套间模型和组件线程模型不匹配的话,COM库将会自动另外创建一个与之匹配的线程套间,并通过代理访问创建的COM对象。所以我们在使用中最好根据组件的线程模型创建相应的线程套间,使程序性能最优化。


 

 

你可能感兴趣的:(多线程,优化,git,null,table,interface)