一、线程、apartment和进程
说道com的线程模型,大家就会想到各种apartment模型。但apartment究竟是什么?如何建立一个apartment呢?
apartment就是线程的容器,线程中有关com的操作必须在apartment中进行。 apartment分为sta和mta两种,sta是只能容纳一个线程的容器,mta是能容纳多个线程的容器。com规定,一个进程中可以有多个sta,但最多只能有一个mta。线程调用coinitializeex(null,coinit_apartmentthreaded)后,这个线程就建立并且进入了一个sta,线程调用coinitializeex(null,coinit_multithreaded)后,这个线程就进入了进程公用mta。一个线程不能同时进入两个apartment。线程调用couninitialize()后,这个线程就退出了它所在的apartment。设计com对象时设定的“apartment模型”就是指这个com对象可以呆在那种apartment中。一个线程建立的com对象自动地呆在这个线程所在的apartment中。要是这个线程建立了很多个com对象,那这些对象都呆在这个线程所在的apartment中。
一个线程可以直接访问它所在的apartment中的com对象,但要访问另一个apartment中的com对象就必须经过调度。因为sta中只有一个线程,别的线程要访问这个线程建立的com对象就必须让这个线程代劳了,如此一来,对这个apartment中所有的com对象的访问都是序列化的,这些com对象就不用担心有好几个线程同时访问它的麻烦事。mta中的com对象就没这么舒服了,它们必须考虑到可能会有好几个线程同时访问它们。mta之外的一个线程访问mta中的一个com对象时,系统会从com系统线程池中取出一个线程进入mta,由它来代表客户线程访问这个com对象。(com系统线程池的机理是怎么样的?池中有几个线程?)
二、客户与服务器
com对象位于服务器中,服务器分为进程内服务器、进程外服务器、远程服务器三种。进程内服务器是一个dll文件,进程外服务器是一个exe文件,远程服务器是另一台计算机上的一个dll文件或exe文件。远程服务器如果是一个dll文件的话,由一个被称为“surrogate”的代理程序调用它。
进程内服务器中的com对象的apartment模型如果与客户线程所在的apartment相配合的话,客户线程建立com对象时会直接建立在客户线程所在的apartment中。比如apartment模型与sta、free模型与mta,both模型与sta或mta。这样客户线程就可以直接调用com对象而不用调度。否则就会专门建立一个线程,然后由这个线程建立com对象,com对象和客户线程就分处在两个apartment中。进程外服务器和远程服务器中的com对象一定不会建立在客户线程所在的apartment中。对它们的调用一定要经过调度的。
三、在c++builder下建立一个多apartment的进程外服务器
由于不必考虑并行的问题,com对象一般设成使用apartment线程模型。进程内服务器还没什么问题,如果你试着建了一个进程外服务器,并且让几个客户同时访问服务器中的对象的话,就会发现这些访问不是同时进行的。如果有一个访问特别费时间,它后面的访问就要等很久才能进行。这是因为服务器中只有一个sta,虽然每个线程都建立了自己的com对象,但这些对象都在这个sta中,当然无法并行执行。
克服这个问题的办法很简单,打开borland\cbuilder5\include\atl\atlmod.h文件,把第266行的:
| typedef tatlmodule 改成: #ifdef __dll__ typedef tatlmodule #else typedef tatlmodule #endif 再打开borland\cbuilder5\include\atl\atlcom.h文件,把第3214行的: declare_classfactory() 改成: #ifdef __dll__ declare_classfactory() #else declare_classfactory_auto_thread() #endif |
| tcommodule projectmodule(0); tcommodule &_module = projectmodule; 改为: tcommodule projectmodule(myinitatlserver); tcommodule &_module = projectmodule; 其中“myinitatlserver”是一个新加的函数,定义如下: void __fastcall myinitatlserver() { if (_module.saveinitproc) _module.saveinitproc(); _module.init(objectmap, sysinit::hinstance, null, 6);//注意这个6 _module.m_threadid = ::getcurrentthreadid(); _module.m_bautomationserver = true; _module.dofileandobjectregistration(); addterminateproc(_module.automationterminateproc); } |
| coinitializeex(null, coinit_apartmentthreaded); imycomobj *pcomobj; olecheck(cocreateinstance(clsid_mycomobj, null, clsctx_local_server , iid_imycomobj, (void **)(&pcomobj))); tcomicomobjinexe acomobj(pcomobj); ……使用acomobj…… couninitialize(); |
Java Asp PHP .Net XML C/C++ CGI VB Jsp J2ee J2se J2me EJB Servlet Tomcat Resin Struts Weblogic Eclipse ANT GUI JMS Web servise IDEA Webphere Hibernate Spring Jboss Applet Swing Socket Javamail Perl Ajax P2P 安全 模式 框架 测试 开源 游戏
Windows XP Windows 2000 Windows 2003 Windows Me Windows 9.x Linux UNIX 注册表 操作系统 服务器 应用服务器