STA和MTA 之 COM和套间( 二 )


套间()的类型
【STA和MTA 之 COM和套间】常见的套间有STA和MTA , 此外中引入了一种新的套间NTA 。STA用于单线程 , MTA用于多线程 。而NTA则被称为线程无关(-)的多线程 。简单来讲 , STA , MTA , NTA的区别请见下表:

STA和MTA 之 COM和套间

文章插图
在文章后面将详细解释各个套间的特点、区别以及编程的有关注意事项 。
线程和套间
线程通过调用/进入套间 , 然后通过退出套间 。进入套间可能会导致套间被创建 , 同样调用会导致套间被释放 。和的调用次数必须Match , 类似/ 。
只能进入STA套间 , 而可以通过传入参数进入不同的套间 , 传入DED进入STA , 而传入则进入MTA 。当调用了/之后 , 线程便属于了这个套间 , 如果指定STA , 那么新的STA总会被创建 , 如果指定的是MTA , 那么如果MTA不存在的话将创建一个新的MTA 。细心的朋友可能已经注意到了 , 上面提到了3种套间 , 那么NTA跑哪去了呢?其实一个线程并不能属于NTA , 线程只可以临时进入NTA , NTA中只可以存在对象 。
对象和套间
COM对象总是属于某个套间的 。COM对象在注册表里面可以通过属性指定对象所期望的套间类型 , 有效的值有:
STA和MTA 之 COM和套间

文章插图
需要说明的是 , 从套间角度来讲主STA和其他非主STA没有区别 , 只是特别指定是主STA而已 。
线程套间和对象套间的关系
大家可以看到 , 线程也有套间 , 同时对象也有套间 , 那么这两者有何关系呢?这是一个比较的一个问题 。事实上 , 简单来讲 , 对象的套间设置决定了对象所处的套间 , 而线程的套间决定了线程的套间 。OK , 看到这里你可能会说 , 这不是等于没说吗?呵呵 , 这确实是最本质的区别 , 然而 , 另外这两个套间的设置还决定了另外一点 , 即套间和对象是否兼容 , 是否处于同一套间 。这很重要 , 因为这决定的了所返回的对象的指针是原始指针还是Proxy(这里讨论进程内的情况 , 进程外则总是Proxy) 。举例来讲 , 如果线程的套间是STA , 并且对象的套间也是STA , 那么这个对象就被创建在线程所位于的STA中 , 反之 , 如果线程的套间是STA , 而对象的套间是MTA , 那么对象则被创建到唯一的MTA套间中 , 线程拿到的是对象的Proxy(代理) , 而非原始指针 。代理的概念后面会讲到 。
MSDN中有一张表 , 这里稍作修改 , 列在下面:
STA和MTA 之 COM和套间

文章插图
跨套间(Cross-) , Proxy/Stub以及
套间调用本套间内的对象不需要Proxy , 则是直接调用 , 和普通C++的虚函数调用并无区别 。COM强大的地方(也是不太容易理解的地方)在于可以通过Proxy来实现线程安全 。我们还是用一个实际的例子来考虑这个问题 , 假如两个MTA线程同时调用一个STA中的对象A , 这个对象因为位于STA中 , 因此它编写的时候没有考虑到多线程问题 , 因此需要保护 。如果两个MTA线程同时通过A的指针pA来调用A的方法 , 显然这个时候是无法提供线程安全的保护的 。COM的解决方案是 , 让这两个MTA线程拿到的对象A并非对象A本身 , 而是A的Proxy 。所谓Proxy , 指的是该对象并非是实际对象 , 而是一个代理 , 负责将调用转发到它所代理的对象A , 代理本身并不执行实际操作 。而在服务器端 , 有一段代码称之为Stub , 负责接受Proxy发来的请求 , 并实际执行这个请求 。换句话说 , Proxy总是在客户端 , 而Stub则是在服务器端 。