COM Apartment (套间)

来源:互联网 发布:python编译器 安卓 编辑:程序博客网 时间:2024/06/11 22:32

COM  Apartment (套间)

套间的由来

最开始的COM库,支持的使用组件的唯一模式是single-thread-per-process模式。这样就避免了多线程的同步,而且组件执行的线程肯定是创建它的线程。
然而组件对象真正的执行环境很复杂。COM组件的执行环境有两种:单线程环境Single-Thread,多线程环境Multi-Thread。单线程要考虑执行线程是否是创建组件的线程;多线程还要考虑并发、同步、死锁、竞争等问题。无论哪种环境,都要编写大量的代码以使COM组件对象正确的运行。
为了使程序员减轻痛苦,COM库决心提供一套机制来帮助程序员。如果我们都遵从这套机制,只要付出较少的劳动,就可以让组件对象和COM库一起完成工作。COM库这套机制的核心技术就是“套间技术”。

COM库的规定

关于多线程问题方面,COM库做出了如下规则(不是COM标准,是COM库为了简化多线程编程中对组件的调用而制定的):
  1. COM库提供两种套间,单线程套间(STA, Single-Threaded Apartment) 和多线程套间(MTA, Multi-Threaded Apartment), COM组件的编写者最好提供对应的属性(线程模型属性, 后面会提到),COM组件的使用者要在套间里创建和调用组件。
  2. COM库对所有的调用进行参数调整(如果需要),不管是对进程内服务器的调用,还是对进程外服务器的调用。
  3. 线程内调用、跨线程调用、跨进程调用都用统一的方式。需要用代理/存根的会用代理/存根。
套间既不是进程,也不是线程。她和进程和线程之间的关联尊崇以下几个原则:
  1. 每个使用COM的进程都有一个或多个套间;
  2. 一个套间只能包含在某一个进程中;
  3. 每个套间可以拥有一个(STA)或多个(MTA)线程;
  4. 一个线程只在某一个套间中执行;
  5. 每个套间可以包含多个对象。
COM规定,只有运行在对象的套间中的线程才能够访问该对象。

STA套间原则:一个进程可以包含0个,1个或者多个STA;每个STA中有且只有一个线程执行;
以上原则决定,驻留在STA中的对象永远也不能被多个线程并发访问,而且只有一个特定的线程可以执行对象的方法。

MTA套间原则:一个进程可以包含0个或者1个MTA;每个MTA中可以有多个线程执行;
以上原则决定,驻留在MTA中对象能够被多个线程并发访问,这在某些情况下可以提供程序的效率,但是作为实现者,你必须处理好线程之间的同步关系。

进程内的主STA套间是进程中第一个调用CoInitialize的线程(主线程)所关联的套间(即进程中的第一个STA套间)。主STA与STA唯一的不同是这是傻瓜型的,连静态和全局变量都可以不用线程保护,因为所有不是安全访问静态和全局变量的对象都通过主线程(第一个调用CoInitialize的线程)的消息派送机制运行,因此不安全的访问都被集中到了一个线程的调用中,因而调用被序列化了,也就实现了对静态和全局变量的线程保护。至于为什么是主线程,因为进程要使用STA,则一定会创建主线程,所以一定可以创建主STA。因此主STA并不是什么第四种套间,只是一个STA套间,不过关联的是主线程而已,由于它可以被用作保护静态和全局变量而被单独提出来说明。因此一个进程内也只有一个主STA套间。

线程分配套间的规则:

如果线程调用CoInitialize,则COM创建新的STA并且将线程放入其中
如果线程调用CoInitializeEx并且传递参数COINIT_APARTMENTTHREADED,则COM创建新的STA并且将线程放入其中
如果线程调用CoInitializeEx并且传递参数COINIT_MULTITHREADED,则COM如果没有MTA,创建新的MTA并且将线程放入其中;如果存在MTA,直接将线程放入其中。

COM对象分配套间的规则:

调用CoCreateInstance或CoGetClassObject等创建对象的函数时,创建的对象将以一个特定规则决定和哪个套间相关联。
决定对象去向的规则如下:当是进程内组件时,根据COM组件注册表项<CLSID>\InprocServer32\ThreadingModel和线程所属套间的不同,列于下表:
Table:COM对象分配套间的规则 创建COM对象的线程
所关联的套间种类COM组件的线程模型属性
(ThreadingModel键值)组件对象最后所在套间1STA""或Single进程内的主STA套间2STAApartment创建线程的套间3STABoth创建线程的套间4STAFree进程内的MTA套间5STA""或Single进程内的主STA套间6STAApartment新建的一个STA套间7STABoth进程内的MTA套间8STAFree进程内的MTA套间

跨套间调用需要汇集操作也就是列集→传输→散集

所有线程模型的算法都是通过代理对象实现的。要跨套间时,使用CoMarshalInterface将代理对象的CLSID和其与组件对象建立联系的一些必要信息(如组件对象的接口指针)列集(Marshaling)到一个IStream*中,再通过任何线程间通信手段(如全局变量等)将IStream*传到要使用的线程中,再用CoUnmarshalInterface散集(Unmarshaling)出接口以获得指向代理对象的接口指针。

套间实现规则

STA 当一个组件是STA时,它必须同步保护全局变量和静态变量,即对全局变量和静态变量的访问应该用临界段或其他同步手段保护,因为操作全局和静态变量的代码可以被多个STA线程同时执行,所以那些代码的地方要进行保护。比如对象计数(注意,不是引用计数),代表当前组件生成的对象个数,当减为零时,组件被卸载。此变量一般被类厂对象使用,还好ATL和MFC已经帮我们实现了缺省类厂,这里一般不用担心,但自定义的全局或静态变量得自己处理。

对象之间数据共享通常有三种方式:DLL中声明全局变量;C++类中的静态成员变量;静态局部变量


MTA 必须对组件中的每个成员和全局及静态变量的访问使用同步手段进行保护,还应考虑线程问题,即不是简单地保护访问即可,还应注意线程导致的错误的操作。

NOTE

有3个STA套间,STA1、STA2和STA3。STA1用CoMarshallInterface得到的IStream*传到STA2中通过CoUnmarshalInterface得到的代理和在STA3中同样通过CoUnmarshalInterface得到的代理不同,不能混用。因为当STA2和STA3调用在STA1的对象时,STA1如果回调(连接点技术就是一种回调)调用者,则STA2和STA3的代理能分别正确的指出需要让哪个线程执行回调操作,即向哪个线程发送消息,因此不能混用。

编写可以工作的COM客户端

规则1:客户线程必须调用CoInitialize[Ex]
规则2:STA线程需要消息循环
规则3:不要在套间之间传递原始未列集的接口指针

有没有不列集需要与其他线程共享的接口指针也OK的情况?有。如果两个线程属于同一个套间,则可以共享原始未列集的接口指针,而这只可能在两个线程都属于MTA时发生。如果不确定是否需要,请进行列集。调用CoMarshalInterThreadInterfaceInStream和CoGetInterfaceAndReleaseStream或者使用GIT总是无害的,因为COM只在必要的时候才进行列集。



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 想开通淘宝直播粉丝不够怎么办 下巴长泡泡还痒怎么办 脚起泡泡很痒怎么办 脚痒还有小泡泡怎么办 外阴长了肉疙瘩怎么办 嘴巴里泡泡破了怎么办 脚上泡泡破了怎么办 脸被自己扣破了怎么办 6s安装不了软件怎么办 苹果6s特别卡怎么办 苹果手机4g网慢怎么办 大王卡玩王者卡怎么办 荣耀7c手机卡顿怎么办 华为6x手机卡顿怎么办 荣耀7c手机老卡怎么办 苹果6打王者卡怎么办 电脑玩游戏显示显卡不行怎么办 笔记本玩游戏显卡不行怎么办 笔记本玩英雄联盟有点卡怎么办 英语考试作文抄了阅读理解怎么办 qq账号被盗怎么办很久了 想玩线上德州没有渠道怎么办 手机玩久了头晕怎么办 玩3d游戏头晕恶心怎么办 win10打cf没声音怎么办 英雄联盟玩家尚未准备就绪怎么办 玩手机想吐应该怎么办 玩手机多了头晕怎么办 玩cf老是无响应怎么办 玩穿越火线好卡怎么办 绝地求生画质卡顿怎么办 手机热点玩lol卡怎么办 一加6直播触手黑屏怎么办 ipad应用商店密码忘记了怎么办 爱派忘记了密码怎么办 爱派id密码忘了怎么办 爱派密码忘了怎么办 爱派的密码忘了怎么办 苹果爱派密码忘了怎么办 鼠标无法识别的usb设备怎么办 电脑鼠标无法识别usb设备怎么办