按照书上说单线程套间模型的COM对象只能在自己的套间里执行,但是我用ATL自己写了一个单线程套间的对象,在我的测试程序里面我从主线程通过COM库函数CreateInstance创建它,然后,开了一个Work thread,把指针传给它,然后在它里面竟然调用成功了,并没有返回书上所说的那个什么WRONG_THREAD的错误!在这个线程里面我甚至都没有调用COM库的初始化函数!这是怎么回事?希望高手解答!同时能把套间的概念解释清除最好了,但请不要用书本上的话,因为我怀疑那些写书的人自己可能都不懂套间的概念呢~~呵呵下面是我的接口,贴上主要代码,我在辅助线程里面调用的就是test函数
class ITest : public IUnknown
{ public: // ... 省略部分无关代码
// ~~~~ 在辅助线程里面调用的它 virtual HRESULT __stdcall test()
{
// 弹个框框~~
MessageBox(NULL,"hey!","",0); };
}
class ITest : public IUnknown
{ public: // ... 省略部分无关代码
// ~~~~ 在辅助线程里面调用的它 virtual HRESULT __stdcall test()
{
// 弹个框框~~
MessageBox(NULL,"hey!","",0); };
}
你在ATL中选择的是 apartment?套间其实是在线程中附加的一个数据段,这部分数据保存在线程的TLS中,TLS中也可以保存其它数据,套间只是给线程附加一些属性.这些属性到底是什么,因为微软没有公开这部分资料(在书上还有MSDN上都找不到该部分资料),所以无法得知它的具体内容.
通过TLS中的这些信息对接口指针进行了保护,要对其它线程内的接口进行访问的时候,必须通过列集,而后再进行散集才能进行访问.
在生成套间的同时,服务器会有些隐含的实现,它会帮接口指针生成一个隐藏的窗口,因为窗口支持消息机制.其它线程对该接口访问的时候,会通过发送消息的机制,将消息传到窗口,这样因为消息有一个队列,是一个一个执行的,就避免了多线程间资源的死所情况.也就使的com开发者无需对自己的数据进行多线程保护.
结论:在辅线程中创建对象成功是可能的,但运行出错是必然的(不是指程序崩溃,而是逻辑错误)举例类似出错情况:
就像多线程访问同一个全局变量,而没有加任何保护一样,安全吗?
COM套间的目的就是:处理并发问题下面简单解释:1.简单来讲:
套间:主要为了同步对组件的调用(内部有个消息循环),没什么复杂的
稍微详细一点:
STA中只有一个线程可以执行,意味着驻留在STA中的对象永远也不可能被并发访问,只有一个特定的线程可以执行对象上的方法,所以对象的实现者可以安全地把多个方法调用用之间地中间状态保存在TLS(线程局部存储)中2.接口指针列集(为什么要列集)
简单来讲:
接口指针只是变换成可被传输的字节流,字节流的唯一内容标志了对象和它所拥有的套间
详细一点:
COM在你列集过程中,会”隐式“产生代理和存根()
在你的辅线程里,真正交互是,你与代理---存根与组件(可惜这里不能贴图,否则很容易明白了) 所有这些操作最主要的目的就是:主线程和辅线程 对组件对象的操作都在同一个套间(可以理解为:向同一个消息队列中发消息)里,最终目的只是处理并发3.下面是一些总结:
套间总结:
(1)组件为 STA
I客户地套间为STA,组件直接生活在客户的STA中,效率最高
II客户的套间为MTA,进程内激活对象迫使COM在另一个套间(默认STA)中激活对象,然后返回给客户一个代理
(2)组件为MTA
I客户的套间为STA,那么类对象(以及后续的实例)将在COM创建的STA中执行
II如果客户的套间为MTA,类对象将在MTA中执行
> 在主线程里调用CoInitialize进行COM库初始化,然后调用CreateInstance生成我的COM对象并返回ITest接口。
> 创建辅助线程,然后把ITest接口指针作为参数传递进去,目的是想在它内部调用ITest->test函数。
> 辅助线程内部并没有调用CoInitialize,直接写了个(ITest*)pParam->test。最后:我执行我的程序,结果是在辅助线程里面弹了出一个框显示"Hey1",这就表示调用接口函数成功了。但是按照道理来讲函数test应该返回一个WRONG_THREAD这样错误才对的,怎么会调用成功了的呢?难道说必须我们自己在接口函数中根据不同的套间模型来预先写判断代码,不符合的就返回WRONG_THREAD吗? 或者说如果不是这种情况的话,那么COM库是在什么时机或者条件下介入接口调用线程模型检查的?
编译期我想是肯定不可能的,因为编译器是不可能识别出指向一个类的指针究竟是接口还是普通的类指针的。
那就必然是在运行期,也就是说是在调用COM库函数之后由它们来做了一些事。那么我就想知道,COM库函数究竟做了哪些事,如何去做的?换句话说就是,调用CreateInstance可能并不像我们所想的只是简单的读注册表、加载DLL、返回指针,肯定还做别的了,究竟做了些什么,我想这可能就是我问题的关键,期待各位高人解答!!!!只要问题能解决,多少分都不是问题! 我想除我以外还会有很多人关心这个问题的。建议版主置顶。
答案是:当然可以这块内存无论是COM对象的真实地址还是通过代理拷贝的栈结构,但对你,都是有效的栈,如果不嫌麻烦,你完全可以用函数指针,就解决问题了COM套间之所以引入,主要目的之一就是处理并发以上只是我的看法,希望大家给予补充与更正
我又仔细看了一下书中所说,看来是我没有领会书中的意思了,书上说在多个线程之间传递接口的话一定要走Marshaling和UnMarshaling这个过程的,估计COM库就是在这里做的手脚了吧。脱离开这一步的话就不会受COM规定的这种套间模型的限制了。原因就像楼上兄弟所说的。而我之前的单纯传递指针的做法确实有点冒险,不提倡这样用。 但是在我的例子里还不至于让程序出问题,因为我知道它在做什么,呵呵~~~