COM线程的一些总结【转贴】

 【Apartent的基本知识】    
  Apartment就是COM中并发访问的边界,是COM的基本线程安全单位。Apartment不    
  能同线程或者进程等Win32对象相比较,只是用于说明COM对象的线程规则:这些规则    
  因为Apartment的类型的不同而不同。它的作用就是提供对于非线程安全的组件提供    
  保护、序列化对于这些组件的访问,我们可以把Apartment想像成一个盒子,里面装    
  着组件和线程,Apartment可以同步对于对于里面的非线程安全组件的并发访问,如    
  果你申明了组件是线程安全的,COM这不会对访问这些对象的线程进行任何同步。    
  每一个要使用COM的线程和每一个这些线程所创建的组件都必须属于一个    
  Apartment,Apartment也从来不跨越进程边界。所以如果一个组件和它的客户端处于    
  不同的进程中,那么它们肯定属于不同的Apartment。当一个客户程序创建Inproc组件    
  时,COM要决定是把组件放在创建线程所属的Apartment中还是放到其他的Apartment    
  中,如果COM把组件放到了创建线程所属的Apartment中,那么线程可以直接访问对象,    
  而当COM把组件放到别的Apartment中时,对于组件的访问就要经过调度(Marshaling)    
  。    
  这就意味着即使使用的Inproc组件,如果客户和组件在不同的Apartment中,也要提供    
  Proxy/Stub对供调度使用,跨Apartment边界的调用和跨进程、跨机器的远程调用使用    
  一样的Proxy/Stub对。    
  目前有三种Apartment,它们分别是STA(Single-Threaded   Apartment),MTA(Multi    
  -Threaded   Apartment),NTA(Neutral-Threaded   Apartment),其中NTA只在Win2k中得到    
  支持。    
  STA中只能属于一个线程,但是对于里面包含的对象的个数是没有限制的,COM也没    
  有限制进程中创建的STA的个数,特别地进程创建的第一个STA称为主STA。STA主要的特    
  性是对于STA中的对象的访问首先交给此STA中的线程处理,由于对STA中的对象的所有    
  访问都在同一个线程上执行,COM就串行化了对非线程安全性的组件的访问。    
  COM在进程第一次调用CoInitilize(NULL)或者对带有COINIT_APARTMENTTHREADED标    
  记的CoInitializeEx函数时会调用RegisterClass来注册OleMainThreadWndClass窗口    
  类,在这期间或者以后对CoInitilize(NULL)或者带有COINIT_APARTMENTTHREADED标记的    
  CoInitializeEx函数调用都将使COM自动地调用CreateWindowEx来为每个STA创建一个具    
  有OleMainThreadWndClass窗口类的隐藏窗口,这个隐藏窗口的消息队列被用来串行化并    
  分派COM方法调用。当COM的RPC通道中出现对STA的方法调用时,COM要给这个STA的隐藏    
  窗口发送表示这个方法调用的消息(message),STA中的线程然后取得此消息,它把这个    
  消息分派到隐藏窗口,隐藏窗口的窗口过程或者把这个调用交给Stub处理,或者直接在    
  组件上执行,因为每次只是取得、分派、处理一个消息,对于STA里的调用就进行了同步    
  。    
  MTA中没有线程个数的限制,任何线程都可以通过声明CoInitializeEx(NULL,COINIT    
  _MULTITHREADED)来进入MTA,另外一点和STA明显不同的是一个进程只能有一个MTA。MTA    
  没有隐藏的窗口也没有消息队列来对调用进行同步,对于MTA中组件的调用出现在PRC线    
  程池中,COM随机地从中选择一个然后交给组件处理。因为没有进行同步,所以MTA中的    
  组件必须是线程安全的。    
  Win2k中引入了另外一种Apartment类型,这就是NTA,每一个进程只能有一个NTA,    
  NTA中不容纳线程,它只是对象的容器,线程不能被赋予到NTA中。NTA的一个特点是对于    
  NTA里面的对象的调用不会引起线程切换,换句话说就是当STA或者MTA中的线程要访问    
  NTA中的对象是,线程暂时地离开STA或者MTA,然后在NTA上执行调用。而STA和MTA上的    
  对象调用都是由Apartment本身里面的线程来执行的,所以跨Apartment的调用必须有线    
  程切换。因为没有线程切换,所以NTA可以认为是对跨Apartment的调用的一种优化。    
  Win2k还提供了一些特别高效的机制来同步调用,这里就不多说了。  

 

 

【线程是如何被赋予到Apartment的:】    
  我们知道在我们的程序中如果要用到COM,就必须调用CoInitilize[Ex],    
  这两个函数的目的就是给线程初始化COM,并且把线程赋予Apartment。而到底赋    
  予到哪个Apartment取决于调用的函数和传递的参数。    
  1、如果线程调用的是CoInitilize,COM就构建一个新的STA,并且把这    
  个线程赋予新的STA。    
  2、如果调用的CoInitializeEx   (NULL,   COINIT_APARTMENTTHREADED),    
  COM做的工作和第一步是一样的。    
  3、如果线程调用CoInitializeEx   (NULL,   COINIT_MULTITHREADED),COM    
  会检查进程中是否存在一个MTA,如果存在就直接把线程赋予MTA,如果还没有MTA    
  存在,则首先创建MTA,然后在把线程赋予该MTA。    
  当COM创建一个Apartment的时候,首先在Heap上创建Apartment对象,然    
  后初始化一些必要的信息(例如:Apartment的ID和类型等等),当COM把线程赋予    
  Apartment时,在线程的TLS上记录线程对象的地址,这样COM库在运行时如果想要    
  知道线程属于那个Apartment,就到线程的TLS上读取Apartment的地址就行了。    
  Inproc组件是如何被赋予到Apartment中的    
  COM读取注册表中组建的ThreadingModel值来决定把在什么Apartment中创    
  建对象,ThreadingModel在InprocServer32分支下,有五种可能的取值,分别是    
  None、Apartment、Free、Both和Neutral(Windows2000才有)。可以用来创建这    
  几种的Apartment类型的对应关系如下所示:    
  None   <-->   主STA    
  Apartment   <-->   任何STA    
  Free   <-->   MTA    
  Both   <-->   MTA   或者   STA    
  Neutral   <-->   NTA(只存在于Win2K中)    
  当线程创建对象的时候,COM总是尽可能地将对象创建到与线程相同的Apartment    
  中,比喻说一个STA中的线程创建ThreadingModel=Apartment的对象,这时候对象    
  将创建在线程所属的STA中;如果MTA中的线程创建ThreadingModel=Free的对象,    
  对象将创建在线程所属的MTA中;然而如果STA中的线程创建ThreadingModel=Free    
  的对象时,对象将创建在进程的MTA中;MTA中的线程创建ThreadingModel=None或    
  者ThreadingModel=Apartment的对象时,对象将创建到STA中。    
  为什么ThreadingModel=None的对象总是在主STA中创建呢?ThreadingModel    
  =   None的对象属于历史遗留问题,产生于COM还不支持多线程的时候,COM为了保    
  证这些对象的线程安全性,就把这些对象都创建在主STA中。试想一下假如有两个    
  readingModel=None的对象在同一个dll文件中,而这些对象要访问dll中的全局数    
  据,COM把对象都创建在主STA中,不是可以防止对象同时读写dll中的全局数据吗?    
  如果写程序的时候声明对象的线程模式为Free或者Both,那么程序员必须保证对    
  象的线程安全性,因为这时候COM不会作任何动作来保证线程安全。    
  【Outproc组件是如何被赋予到Apartment中的】    
  进出外组件没有TheadingModel属性,这是因为COM采用了一种完全不同的    
  方法来把组件赋予到Apartment,一般来说COM把组件创建在服务器进程中的创建    
  组件的线程所属的Apartment中。大多数Exe   COM服务器在它们的主线程中调用    
  CoInitilize[Ex]以使主线程属于主STA,然后主线程调用CoRegisterClassObject    
  来注册类工厂,当服务器接受到激活请求时,创建组件的请求在主STA得到处理,    
  因而对象也是在主STA中创建的。    
  如果希望在MTA中创建对象,那么就必修把调用相应CoRegisterClassObject    
  的线程赋予到MTA中,这样激活请求在MTA中得到处理,在MTA中创建组件。    
  综上所述:对于Exe   COM服务器,调用CoRegisterClassObject的线程所属的    
  Apartment中的类工厂创建的组件属于这个Apartment。这其中也有少量例外的情    
  况:用ATL些的组件如果CComAutoThreadModule和CComClassFactoryAutoThread有    
  所不同,关于ATL的具体介绍,过几天我想专门写几篇文章说说,这里就不在写了。    
  【总结】    
  1、每个STA只能有一个线程,但是每个进程的STA数量是没有限制的。对于STA    
  中方法调用,总是被送到STA中唯一的线程来处理,所以STA一次只能接收和处理    
  一个方法调用。    
  2、MTA中可以有任意多的线程,但是每个进程只能有一个MTA。对于MTA中的方    
  法调用将不被同步地分派,所以MTA中的对象和线程必须是线程安全的。    
  3、NTA中没有线程,而且每个进程只能有一NTA。对于NTA中的方法调用不会引    
  起线程切换,调用的线程暂时离开自己的Apartment,在NTA上直接执行调用。  

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值