COM套间

[1] COM套间的产生原因

COM组件由于经常在系统中只存在一个实例,给出的多个接口事实上只是对一个对象实例的引用,所以,在多线程的情况下,会存在重入问题。

COM组件的作者有可能在他的代码中做了互斥处理,也有可能没有做互斥处理。当他没有做互斥处理的时候,为了保证这个组件也能在多线程情况下使用,微软提出了COM套间的概念。通过对COM组件使用不用的COM套间,来使COM组件的访问达到安全与高效的平衡。

 

[2] COM套间的实质

套间从技术上讲,只是保存在某个线程TLS上的一个数据结构(这个数据结构微软没有发布),COM库通过访问这个数据结构,通过窗口消息机制来实现多线程下COM组件的调用机制。

 

[3] COM套间的种类以及套间线程的种类

     COM组件在被作者注册到注册表里面的时候,可以对每个接口分别注册套间类型,一共有:Single、apartment、free、both四种COM套间类型。

     根据不同的COM组件类型,客户在调用之前必须在线程中构造单线程套间或者多线程套间,线程套间的构造函数为:

HRESULT CoInitializeEx(void * pvReserved, DWORD dwCoInit);

其中第一个参数为保留字段,必须为NULL。第二个字段一般取如下两个值:

n COINIT_APARTMENTTHREADED:这个参数说明当前套间为单线程套间,套间类型为Single和apartment的COM组件应该在这种线程中创建。

n COINIT_MULTITHREADED:这个参数说明当前套间为多线程套间,套间类型为free的COM组件应该在这种线程中创建。

上面这两个参数是互斥的,即选了一个就不能选另外一个。选定上面的某个参数之后,另外还可以用|来补选两个可选参数:

n COINIT_DISABLE_OLE1DDE :表明OLE1支持中使用DDE

n COINIT_SPEED_OVER_MEMORY:利用空间换取时间,即COM的运行效率更高。

 

[4] single组件和apartment组件的特征

当一个COM组件在注册表中标明为Single或apartment类型时,说明该COM组件不是线程安全的,即不可重入,这时,创建他们的组件应该使用COINIT_APARTMENTTHREADED参数来构造套间。

当在单线程套间里面构造了这两种组件后,如果你直接把COM对象的接口指针通过共享内存传递到另外一个线程中然后使用的话,程序是会出错的,我记得当时我犯这种错的时候是函数的调用不起作用,VS会提醒你使用了一个非法的句柄。

如果需要在另外一个线程中使用该接口,只能通过COM提供的API来传递接口指针(即COM中的散列集),具体操作是现在创建COM对象的线程中先调用CoMarshalInterThreadInterfaceInStream函数将接口转化为一个流,然后在另外一个线程中使用CoGetInterfaceAndReleaseStream函数来从流中获得接口指针。

 

[5] Single组件和apartment组件的不同

Single组件和apartment组件不同之处在于,一个进程中最多只允许存在一个Single组件的实例,而允许存在多个apartment组件的实例。即在多个线程中调用CoCreateInstance函数的话,如果组件是Single套间类型的话,获得的接口均指向同一个COM实例;如果组件是apartment套间类型的话,各个线程将拥有自己不同的组件实例。

 

[6] Single组件和apartment组件的多线程调用

当客户在线程A中创建了COM对象,在线程B中通过散列集获得了该COM组件的接口然后调用时,这种调用并不是直接调用的。

在创建单线程套间的线程A中,线程A在构造套间的时候同时构造了一个隐形窗口,当线程B调用CON组件的某个方法时,实际上是通过发送消息给该隐形窗口,让线程A执行。

也就是说,通过窗口消息的形式,COM库实现了Single组件和apartment组件的线程安全。

 

[7] Free组件的特征

当一个COM组件在注册表中说自己是Free类型的时候,说明它是线程安全的,客户在调用它的时候无须担心重入问题。

Free组件应该被创建多线程套间的线程来创建。即CoInitializeEx中的参数为COINIT_MULTITHREADED,在这种情况下,通过共享内存来传递接口是可行的。并且多线程访问改COM组件时,函数的调用没有经过窗口机制,非常高效。

多线程套间其实没有做什么事情~~。

 

[8] Both组件的特征

顾名思义,Both的意思就是多线程套间和单线程套间均可使用,所以Both组件也是线程安全的。

 

[9] 线程创建的套间与COM组件套间类型不匹配时怎么办?

上面讲到,相应套间类型的COM组件必须在创建了该套间类型的线程中被创建。但是,如果当这两种类型不匹配的时候会出现什么情况呢?

答案是,当类型不匹配时,COM库会自己构造缺省的与COM组件套间类型相符的线程,来创建COM对象,以维护COM组件的安全性。

比如,如果我们在线程中创建多线程套间,然后在线程中创建single或者apartment类型的组件。此时,COM库会新增一个创建了单线程套间的线程,在该线程中创建该COM组件,保持COM组件的单线程特性,其他线程对该COM组件的调用均通过消息机制进行。

所以,如果在线程中创建多线程套间后创建single或者apartment类型的组件时,接口函数的调用将变得非常低效。

 

[10] 为什么需要Both类型

Both类型的存在,是为了在某些情况下提高COM组件的调用效率,比如,当我们在组件A中调用了组件B,而组件A是Free类型,处于多线程套间,而组件B是Single类型,只能处于单线程套间。此时A对B的调用只能跨套间调用,损失不少效率。

如果将A设置为Both类型,那么在线程创建套间的时候就可以直接创建单线程套间,此时A对B的调用不会跨越套间,从而提高了调用效率。

 

1、如果一个接口直接作为进程共享,多线程调用会出现什么情况?

如果这个接口实现的时候考虑到了多线程调用加了锁,那么多线程调用不会出问题;如果这个接口实现的时候没有考虑多线程调用,那么多线程调用有可能出现重入,执行的过程中会出现无法估计的错误,需要marshall和unmarshall,它们会通过消息循环实现同步。

2、线程初始化的时候通过CoInitialize可以设置它的套件属性(STA、MTA等),而接口在注册的时候也设置了它的属性(在注册表中),所以会出现线程创建的套间与COM组件套间类型不匹配时怎么办?

3、对于自己封装的伪COM,因为没有套件属性,所以设计的时候需要考虑并发和同步。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值