您的位置:首页 > 其它

COM 经验的八个教训(2):不要在线程之间传递原始接口指针

2005-03-12 10:36 330 查看

不要在线程之间传递原始接口指针

我咨询的首批 COM 项目之一就涉及到一个包含 100,000 行代码的分布式应用程序,该程序是由美国西海岸的一个大型软件公司编写的。该应用程序在多个机器上创建了数十个 COM 对象,并从客户端进程启动的背景线程中调用这些对象。开发小组遇到问题了,调用要么消失得无影无踪,要么在没有明显原因的情况下失败。他们给我演示的最惊人的症状是:当一个调用无法返回时,在同一台机器上启动其他支持 COM 的应用程序(包括 Microsoft Paint 等)会频繁导致这些应用程序被锁定。检查他们的代码后发现,他们违反了 COM 并发的一个基本规则,就是说,如果一个线程要与另一个线程共享一个接口指针,它应首先封送该接口指针。如果有必要,封送接口指针可使 COM 创建一个新的代理(以及一个新的信道对象,将代理和存根结对),以允许从另一个单元向外调用。不通过封送而将原始接口指针(内存中的一个 32 位地址)传递给另一个线程,会绕过 COM 的并发机制,并且如果发送和接收的线程位于不同的单元中,将出现各种不良行为。(在 Windows 2000 中,由于两个对象可以共享一个单元,但又位于不同的上下文中,因此如果线程位于同一个单元中,可能会使您陷入困境。)典型的症状包括调用失败和返回 RPC_E_WRONG_THREAD_ERROR。Windows NT 4.0 和更高版本可以使用一对名为 CoMarshalInterThreadInterfaceInStream 和 CoGetInterfaceAndReleaseStream 的 API 函数,在线程之间轻松地封送接口指针。假定您应用程序中的一个线程(线程 A)创建了一个 COM 对象,继而接收了一个 IFoo 接口指针,并且同一进程中的另一个线程(线程 B)想调用这个对象。在准备将接口指针传递给线程 B 时,线程 A 应该封送该接口指针,如下所示:
在 CoMarshalInterThreadInterfaceInStream 返回后,线程 B 就可以安全地取消封送该接口指针:
在这些示例中,pFoo 是一个 IFoo 接口指针,pStream 是一个 IStream 接口指针。COM 在调用 CoMarshalInterThreadInterfaceInStream 时初始化 IStream 接口指针,然后在 CoGetInterfaceAndReleaseStream 内部使用和释放该接口指针。实际上,您通常要使用一个事件或其他同步化基元来协调这两个线程的行为 — 例如,让线程 B 知道接口指针已准备好,可以取消封送。请注意,以这种方式封送接口指针不会出现任何问题,因为 COM 有足够的智能,在不需要进行封送时不会去封送(或重新封送)指针。如果在线程之间传递接口指针时这样做,使用 COM 就轻松多了。如果调用 CoMarshalInterThreadInterfaceInStream 和 CoGetInterfaceAndReleaseStream 看起来太麻烦,您还可以通过将接口指针放在全局接口表 (GIT) 中,并让其他线程去那里检索它们,从而实现在线程之间传递接口指针。从 GIT 中检索到的接口指针在被检索时会自动封送。更多信息,请参阅 IGlobalInterfaceTable 中的文档。请注意,GIT 只存在于 Windows NT 4.0 Service Pack 4 及更高版本中。
                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息