原文:COM的多线程模型

COM的多线程模型是COM技术里头最难以理解的部分之一,很多书都有涉及但是都没有很好的讲清楚。很多新人都会在这里觉得很迷惑,google大神能搜到一篇vckbase上的文章,但是个人建议还是不要看的好几乎是胡说八道在乱搞。

COM自己其实并没有任何多线程模型,所以他用的多线程模型还是WIN32里头的那一套线程和同步对象。作为准备,这里先简单讲一下WIN32的线程和同步。作为惯例一讲WIN32的线程和同步对象就要把进程、线程这两个东西讲一遍,但是这里不讲,因为会看COM的对这部分已经很熟悉了,如果不熟悉的话建议也不要看COM了先回头看看《Windows核心编程》和《Windows高级编程》。WIN32的线程可以分为两种,UI线程和工作线程。UI线程是一种与一个窗口绑定的线程,其特点是包含一个窗口一个消息循环和一个窗口过程,由于消息循环的存在导致了其天生就具有一种同步机制:任何发送到该线程的消息都会被消息循环同步,不会有任何两个或以上的消息同时被窗口过程处理,所有消息都会被消息循环串行化;工作线程则可以认为是一个函数在一个线程上的一次运行,这种线程不具备任何自带的同步机制,如果要对两个工作者线程实施某种同步则只能使用WIN32的同步对象如CriticalSection或者Event等等。

接下来看COM的多线程模型,从VS2005的ATL工程向导上可以看到COM多线程模型分为这么几类:单线程(Single)、套间(Apartment)、两者(Both)、自由(Free)。这个部分个人觉得翻译不是很好,单线程(Single)个人认为翻译成单套间会比较好,原因后面有具体描述,但是作为尊重MS向导或者不至于更加把这部分弄得混乱,下面的术语还是引用MS向导上的讲法并且我尽可能使用英文术语。

可以看到COM多线程模型里最多用到的两个字是套间,那么先解释一下套间。套间可以根据他的英文想象一个房间,这个房间周围有墙,所以要进到这个房间必须用一种手段来穿透(通过门或者类似的东西),而这个房间里放的就是一个或者多个COM对象。对套间更理论性的解释是,在一个套间内存在一个或多个COM组件,而套间之间存在有一个明确的界限,并且套间内只存在唯一的一个套间线程,这个套间线程存在一个类似于消息循环(其实不应该用类似的,他就是一个隐藏了窗口的消息循环)来保证其天生所具有的同步性。看了这个定义你会觉得套间像什么?没错,一个只有一个主线程的Windows窗口应用程序进程。所以套间就是一个UI线程!做为UI线程他自然就能完成同步的功能。接下去分几个部分来讲这几种COM线程模型。

一、单线程(Single)

前面讲过这个模型最好是被翻译成单套间的好,因为这种多线程模型并不是说COM组件只能被用在单线程程序里头的,相反组件还是可以被正常的用在多线程程序里的。这种模型的真实意义是即使你的程序是多线程的并且在每个线程里都调用了CoInitalize(0,COINIT_APARTMENT),事实上在你的程序进程里头也只创建一个套间,并且把所有的组件都放到这个套间里头并由这个套间所拥有的消息循环来保证同步性。

或许这样讲不全面,但是上面一段确实讲了一种最简单的情况,就是所有的组件都按Single模型来创建。如果不是这样会什么情况呢,举个例子说A、B、C三个组件按Single模型创建,D按Apartment模型创建,并且四个组件分别在TA、TB、TC、TD四个线程里创建实例(每个线程都调用CoInitalize(0,COINIT_APARTMENT)来创建环境),那么组件A、B、C运行在由TA创建的套间里(TB、TC都没有创建套间,TA是这个套间的套间线程),而组件D则独立运行在TD创建的套间里(TD是这个套间的套间线程),这里一共就有了两个套间。这样应该是完整的情况了,再复杂的情况我想你都能推出来了。

二、套间(Apartment)

这种模型与前一种模型很相似,可以都被认为是创建WIN32概念上的UI线程,但是不同的在于,Single模型无论你在多少个线程里调用多少次CoInitalize(0,COINIT_APARTMENT)都只创建一个套间,套间的套间线程是你第一次调用CoInitalize(0,COINIT_APARTMENT)的线程,而Apartment模型则是你在一个线程上调用一个CoInitalize(0,COINIT_APARTMENT)就创建一个套间,并且把这个线程作为套间线程。

三、自由(Free)

这种模型就是工作者线程了,COM不再用消息循环来提供同步机制了。你要在多线程里使用,OK,那你自己给他做同步机制(或者由组件开发者把组件做成线程安全的)。你要单线程里使用,那更好无论如何都不需要同步了。

四、两者(Both)

这种模型保证了组件即能在套间模型使用也能在自由模型使用。例如说组件自身被创建为Apartment或者Single,但是使用者用CoInitalize(0,COINIT_MULITITHREAD)来创建环境,那么COM自己会再创建一个线程用CoInitalize(0,COINIT_APARTMENT)来创建环境供组件运行,反之亦然。

最后讲一下跨套间调用的问题,从上面的描述可以看到在Single和Apartment这两种模型里头套间内调用或者通过COM机制套间之间的通信都会被同步化,但是如何跨套间调用呢,比如说我们经常做这种事情,把一个存在在套间内组件的接口指针作为线程参数传递到另外一个线程中使用,或者两个组件存在于不同的套间中,但是由于连接点或者回调的原因需要互相调用。这个时候我们就需要使用proxy/stub机制,在传递接口指针之前调用CoMashalInterThreadInterface这个函数(我估计这个是所有WIN32API里函数名最长的函数了,MS真不让人过好日子-.-|||)来包装接口指针给PS dll来传递,然后再那个调用的线程或者套间里使用CoGetInterfaceAndReleaseStream来重新获得被调度过的接口指针,这样就能确保COM的线程同步机制能够正常的运行。

好了,全部讲完了,如果还不清楚建议去看一下《COM技术内幕》里的第十二章,这本书是我见过的所有书里对这部分描述得最好的一本(胜过《ATL技术内幕》),特别是里面那几张图,对理解这些模型非常有帮助,这书网上有电子版。

【转载】COM的多线程模型的更多相关文章

  1. Muduo 多线程模型对比

    本文主要对比Muduo多线程模型方案8 和方案9 . 方案8:reactor + thread pool ,有一个线程来充当reactor 接受连接分发事件,将要处理的事件分配给thread pool ...

  2. Chrome多线程模型

    为什么使用多线程? Chrome的多线程模型主要解决什么问题? 如何实现该问题的解决? 1. 解决问题 Chrome有很多线程,这是为了保持UI线程(主线程)的高响应度,防止被其他费时的操作阻碍从而影 ...

  3. Oracle12c(12.1)中性能优化&功能增强之通过参数THREADED_EXECTION使用多线程模型

    1.   后台 UNIX/Linux系统上,oracle用多进程模型.例如:linux上一个常规安装的数据库会有如下进程列: $ ps -ef | grep [o]ra_ oracle  15356  ...

  4. OS之进程管理---多线程模型和线程库(POSIX PTread)

    多线程简介 线程是CPU使用的基本单元,包括线程ID,程序计数器.寄存器组.各自的堆栈等,在相同线程组中,所有线程共享进程代码段,数据段和其他系统资源. 传统的的单线程模式是每一个进程只能单个控制线程 ...

  5. 第13章 TCP编程(4)_基于自定义协议的多线程模型

    7. 基于自定义协议的多线程模型 (1)服务端编程 ①主线程负责调用accept与客户端连接 ②当接受客户端连接后,创建子线程来服务客户端,以处理多客户端的并发访问. ③服务端接到的客户端信息后,回显 ...

  6. Java NIO学习与记录(八): Reactor两种多线程模型的实现

    Reactor两种多线程模型的实现 注:本篇文章例子基于上一篇进行:Java NIO学习与记录(七): Reactor单线程模型的实现 紧接着上篇Reactor单线程模型的例子来,假设Handler的 ...

  7. 谈谈dpdk应用层包处理程序的多进程和多线程模型选择时的若干考虑

    看到知乎上有个关于linux多进程.多线程的讨论:http://www.zhihu.com/question/19903801/answer/14842584 自己项目里也对这个问题有过很多探讨和测试 ...

  8. [转载]sklearn多分类模型

    [转载]sklearn多分类模型 这篇文章很好地说明了利用sklearn解决多分类问题时的implement层面的内容:https://www.jianshu.com/p/b2c95f13a9ae.我 ...

  9. [源码分析] 分布式任务队列 Celery 多线程模型 之 子进程

    [源码分析] 分布式任务队列 Celery 多线程模型 之 子进程 目录 [源码分析] 分布式任务队列 Celery 多线程模型 之 子进程 0x00 摘要 0x01 前文回顾 1.1 基类作用 1. ...

随机推荐

  1. ajax“显示弹窗详情”和“删除”功能练习

    1.查看详细信息,以弹窗的形式显示,使用ajax 2.批量删除 “查询”功能可以参考前面的文章,这里只讲解ajax“显示弹窗详情”和“删除”功能 第一:在body中的代码 <title>a ...

  2. [php] PHP Fatal error: Class 'AMQPConnection' not found

    When using rabbitmq, $this->conn = new AMQPConnection($conn_args); $this->conn->connect(); ...

  3. 图像处理工具包ImagXpress中如何定义查看器的属性

    想要在图像处理控件ImagXpress中查看一个图像,首先需要创建一个查看器,之后你可以按照你自身的需要,来定义查看器的属性. 创建查看器 想要动态的创建一个查看器,需要先定义一个新的mageXVie ...

  4. android 项目学习随笔十五(ShareSDK开放平台)

    ShareSDK开放平台http://www.mob.com/#/

  5. linux网站目录及Apache权限的设置

    apache服务器访问权限设置禁止所有访问:Options Indexes FollowSymLinks 改为 Option None   Apache单个或多个目录禁止访问方法   这种方法通常用来 ...

  6. javaWeb---Servlet

    1.整个Servlet页面跳转访问流程: 1.1:依据form表单的action的值找到web.xml中servlet-mapping的url的值找到对应的java类,在根据form中的method属 ...

  7. win32 treeview

    // 1.create treeview DWORD dwStryle = WS_VISIBLE | WS_CHILD | TVS_HASLINES|TVS_SHOWSELALWAYS/*|TVS_L ...

  8. IEnumerator:概念详解

    IEnumerable接口是非常的简单,只包含一个抽象的方法GetEnumerator(),它返回一个可用于循环访问集合的IEnumerator对象.IEnumerator对象有什么呢?它是一个真正的 ...

  9. MySQL 5.7 SYS系统SCHEMA

    版权声明:本文为博主原创文章,未经博主允许不得转载. 在说明系统数据库之前,先来看下MySQL在数据字典方面的演变历史:MySQL4.1 提供了information_schema 数据字典.从此可以 ...

  10. php中替换函数主要用的几个函数strtr(),str_repalce()。

    php中替换函数主要有strtr(),str_repalce()这两个函数,今天介绍下他们的区别和用法, 先来看看这个php字符串替换函数 strtr()的两种用法: strtr(string,fro ...