用C++Builder建立多线程COM服务器

C++builder 中的多线程 在C++Builer中多线程的实现           还在Dos时代,人们就在寻求一种多任务的实现。于是出现了TSR类型的后台驻留程序,比较有代表性的有Side       Kick、Vsafe等优秀的TSR程序,这类程序的出现和应用确实给用户使用计算机带来了极大的方便,比如Side       Kick,我们编程可以在不用进编辑程序的状态下,一边编辑源程序,一边编译运行,非常方便。但是,Dos 阅读详情
用C++Builder建立多线程COM服务器
2002-08-12· · ··C++builder资源中心

一、线程、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 TComModule;

改成:

#ifdef __DLL__

typedef TATLModule TComModule;

#else

typedef TATLModule > TComModule;

#endif

再打开Borland/CBuilder5/Include/Atl/Atlcom.h文件,把第3214行的:

DECLARE_CLASSFACTORY()

改成:

#ifdef __DLL__

DECLARE_CLASSFACTORY()

#else

DECLARE_CLASSFACTORY_AUTO_THREAD()

#endif

就可以了。重新编译你的程序,同时开两个客户试一试,是不是并发执行了?

先别高兴得太早,如果你同时开了五个客户,并且其中四个在执行费时的访问,你就会发现第五个客户的访问要等待一段时间。这种现象与C++Builder的实现代码有关。

作了前面的修改后,服务器启动后会预先生成几个线程,这些线程各自进入一个STA中。当服务器接到客户的访问要求后,会循环指定一个线程负责这个客户的建立COM对象、访问COM对象的事务。

比如第一个客户要求建立一个COM对象,服务器就给一号线程发消息,让这个线程建立一个COM对象并把这个COM对象的接口传给客户,以后第一个客户对这个COM对象的访问就全由一号线程代理。而第二个客户的建立COM对象、访问COM对象的事务就由服务器指定二号线程来办,如果客户太多,线程用完了,服务器又会让一号线程负责客户的要求,依次循环。如果客户很多,线程可能会负责几个客户的访问要求,而由同一个线程服务的客户的访问就会顺序执行。预先生成的线程数缺省为系统的CPU个数乘以四,也就是四个(除非你的机器有好几个CPU)。

只能同时服务四个客户当然是不行的,让我们继续修改。打开主CPP文件,可以看到下面两行代码:

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);

}

看到那个6没有,这代表服务器启动后会预先生成6个线程,也就能同时服务6个客户。这个6可以改成别的数,当然不要太大了,不然机器垮了可别怪我。

改到现在你可能比较满意了,但其实这个服务器还是有缺陷:一开始就生成所有线程是不是太浪费了?循环分配线程好象也不太合理,更重要的是,如果客户程序中途垮了,没有Release它建立的COM对象,那这个COM对象将一直存在下去,占用的资源无法收回。

要解决这些问题就比较麻烦了,建议大家看一看ATL源代码,编写自己的TComModule类和CComThreadAllocator类。

四、编写多线程客户程序时要注意的问题

建立客户程序时必须包含的*_ATL.h文件中有一个很好的COM对象包装类。比如我建立了一个ComLib服务器,里面有一个MyComObj对象,那么在ComLib_ATL.h文件中有一个TCOMIMyComObj类,它很好的封装了MyComObj对象。写单线程程序时可以这样建立它:

TCOMIMyComObj aComObj = CoMyComObj::CreateInstance();

(CoMyComObj是定义在在ComLib_ATL.h文件中的一个辅助类)然后就可以使用aComObj了,不必调用CoInitializeEx()和CoUninitialize(),也不必释放aComObj。假设MyComObj对象中定义了一个方法fun(),一个属性num,可以这样使用:

aComObj.fun();

aComObj.num = 14;

int val = aComObj.num;

注意到num的访问方法了吗?C++Builder灵活运用了特有的__property关键字,不必调用get_num()和set_num()了。

如果在写多线程客户程序时也这样就会出问题:除了第一个线程正常外,后面的的线程无法建立COM对象了。

问题出在CoMyComObj里面,它保证了会调用CoInitializeEx()和CoUninitialize()并且在整个进程中只会调用一次。而在多线程客户程序中,每个线程都必须调用CoInitializeEx()和CoUninitialize()一次。因此,除了第一个线程成功进入了Apartment,别的线程都失败了。

可以这样建立TCOMIMyComObj对象:

CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

IMyComObj *pComObj;

OleCheck(CoCreateInstance(CLSID_MyComObj, NULL, CLSCTX_LOCAL_SERVER

, IID_IMyComObj, (void **)(&pComObj)));

TCOMIComObjInExe aComObj(pComObj);

……使用aComObj……

CoUninitialize();

注意,这段代码必须写在TThread::Execute()中,因为只有TThread::Execute()里的代码才是真正运行在新线程中的。另外决不能调用pComObj->Release()。

后记

学COM的念头起于看李维写的那三本书中的第一本的时候,李维描述了建立多线程服务器的重要性,但具体方法只是一笔带过。后来我看了Delphi带的例子,想用在C++Builder中,却无从下手。在关于COM的部分,Delphi和C++Builder相差太大了,而又没有这方面的C++Builder的书,网上的资料也很少,只好自己摸索。期间曾在网上发贴提问,总是没人回答,痛苦啊!
C++Builder OLE自动化Excel:从基础封装到大数据报表实战 OLE(对象链接与嵌入)是Windows平台上实现应用程序间自动化交互的核心COM技术,其原理是通过标准的接口协议,允许一个程序(如C++Builder应用)创建并控制另一个程序(如Excel)的对象模型,实现无缝操作。这项技术的工程价值在于,它能让原生C++应用在不依赖外部脚本或中间件的情况下,深度集成并自动化Office套件的复杂功能,从而在工业控制、数据采集和企业级系统中,高效完成报表生成、数据分析与可视化等任务。针对C++Builder开发环境,通过导入Excel类型库并封装可复用的OLE辅助类,开 阅读详情

相关推荐

C++Builder图像处理实战:从VCL组件到算法优化与OpenCV集成

图像处理作为计算机视觉的基础,其核心在于对像素数据的数学运算与空间变换,以实现平滑、锐化、边缘检测等效果。在Windows桌面应用开发中,选择合适的工具链能极大提升开发效率与性能平衡。C++Builder凭借其成熟的VCL组件库,为图像处理提供了TBitmap与TCanvas等原生支持,开发者可直接操作ScanLine进行高性能像素遍历,实现均值滤波、中值滤波等经典算法。通过TParallel::For进行多线程优化,并结合OpenCV集成,能够将传统图像处理与现代计算机视觉库无缝衔接,满足工业质检、医学影

GOBY 栏目 501

C++ builder 多线程实例(生产-消费)

根据网上生产消费模型改写的易于理解(自己)的线程例子。

C++ Builder 6 源代码深度解析:从经典VCL框架到现代工程实践

在桌面应用开发领域,快速应用开发(RAD)框架通过可视化组件和事件驱动编程,极大提升了软件构建效率。其核心原理在于将界面设计与业务逻辑分离,通过封装底层系统API(如Windows消息机制和图形设备接口)为可重用的控件库。这种范式不仅加速了原型开发,更在企业级应用、工控系统和数据库前端等场景中证明了其工程价值。以经典的VCL框架为例,它代表了RAD理念在C++环境下的高峰实践。本文聚焦于对C++ Builder 6(BCB6)这一特定历史版本所附带的示例源代码进行体系化研究,旨在挖掘其中关于**数据库访问层

weixin_34163553的博客 331

c++ builder 多线程

c++ builder 多线程方法和实例,

C++ Builder 6实战教程:从零构建Windows桌面应用,掌握C++与VCL核心开发

C++作为一门经典的面向对象编程语言,其核心价值在于对内存管理、指针操作和底层系统调用的精细控制,这使得它在需要高性能和硬件直接交互的桌面应用开发中依然占据重要地位。其技术原理通过类、继承、多态等机制,实现了代码的模块化和复用,为构建复杂软件系统提供了坚实基础。在工程实践中,快速应用程序开发工具能够显著提升GUI程序的开发效率,通过可视化拖拽和事件驱动编程模型,将界面设计与业务逻辑紧密结合。C++ Builder 6正是这样一款经典的RAD工具,它基于VCL组件库,让开发者能够在直观的IDE环境中,快速构建

weixin_30621711的博客 345

C++Builder实战:从源码到精通,手把手构建桌面应用

在桌面应用开发领域,快速应用开发(RAD)是一种通过可视化工具和组件库高效构建软件的方法论。其核心原理在于将界面设计、业务逻辑与底层代码解耦,通过事件驱动模型和组件复用,大幅提升开发效率。这种模式的技术价值在于让开发者能聚焦业务实现,而非底层细节,尤其适合需要快速迭代的Windows平台工具、数据库管理系统和企业内部应用。C++Builder作为经典的RAD工具,结合了C++的性能优势与VCL组件的丰富性,是学习这一开发范式的理想平台。通过源码驱动的实战学习,开发者可以深入理解从数据库操作到界面绑定的完整流

weixin_34199335的博客 456

在BCB中使用多线程 ( 转载 )

多线程编程是提高系统资源利用率的一种常见方式。它占用的资源更小,启动更快,还可以实现在后台运行一些需时较长的操作。一、初识TThread对象VCL提供了用于多线程编程的TThread类,在这个类中封装了Windows关于线程机制的Windows API,通常将它的实例成为线程对象。线程对象通过封装简化了多线程应用程序的编写。注意,线程对象不允许控制线程堆栈的大小或安全属性。若需要控制这 些,必须使

2352

C++Builder 6 核心架构与实战开发指南:从VCL框架到数据库应用

快速应用程序开发(RAD)是一种通过可视化工具和组件库提升软件构建效率的经典范式,其核心原理在于将界面设计与业务逻辑分离,通过事件驱动模型简化开发流程。这一理念在桌面应用开发中具有重要技术价值,尤其适用于需要快速构建图形用户界面(GUI)的工业控制、数据采集和管理信息系统等场景。C++Builder 6作为该范式的代表性工具,其内置的VCL(可视化组件库)框架和高效的C++编译器,使得开发者能够通过拖拽组件和编写事件处理代码,快速实现功能完备的应用程序。本文聚焦于C++Builder 6的**VCL框架**

weixin_34343000的博客 382

C++Builder 2009深度解析:VCL框架、数据库开发与多线程编程实战技巧

在Windows桌面应用开发领域,快速应用开发(RAD)理念通过可视化组件和事件驱动编程,显著提升了软件构建效率。其核心原理在于将界面元素、数据绑定和业务逻辑进行封装,通过属性-事件模型实现高效交互。这一技术价值在于能够以更少的代码量快速构建功能复杂、界面专业的应用程序,特别适合维护遗留系统或开发特定行业的工具软件。应用场景广泛覆盖工控、数据采集、仪器仪表等需要稳定高效Windows桌面解决方案的领域。本文聚焦于C++Builder 2009这一经典RAD工具,深入探讨其VCL框架的精髓使用心法,详解TCl

weixin_34248487的博客 279

C++ 操作 Word 文档 2 大方案对比:COM自动化 vs 第三方库(Aspose.Words)

本文深入对比了C++操作Word文档的两种主流方案:COM自动化和第三方库(如Aspose.Words)。COM自动化通过Microsoft Office提供完整功能但依赖性强,而Aspose.Words则支持无依赖部署和跨平台操作。文章详细分析了两者在功能特性、性能指标、部署要求及成本方面的差异,并提供了实战场景建议,帮助开发者根据项目需求做出最优选择。

weixin_34253126的博客 398

Borland C++ Builder 6.0:经典RAD工具在现代环境下的配置与实战指南

快速应用程序开发(RAD)是一种通过可视化组件和模型驱动方法提升软件构建效率的经典范式,其核心原理在于将重复性界面编码转化为拖拽式设计,从而让开发者聚焦业务逻辑实现。在技术价值层面,RAD工具显著降低了GUI应用程序的开发门槛,尤其适合需要快速迭代的内部工具和工业控制软件。虽然现代跨平台框架已成主流,但在维护遗留系统、教学演示或需要轻量级原生执行的特定场景中,基于VCL(Visual Component Library)的经典RAD环境仍具独特优势。本文以Borland C++ Builder 6.0为例,

weixin_30600197的博客 453

Delphi/C++Builder下免Office的Excel文件处理源码(XLS/XLSX全支持,含样式、公式、PDF导出)

一套专为Delphi和C++Builder设计的Excel文件处理工具源码,无需安装Microsoft Excel或Office组件,直接读写.xls和.xlsx格式。支持单元格数据导入导出、字体/边框/背景等样式设置、Excel公式解析与计算、自动筛选、基础图表操作、CSV格式转换、ZIP/LZ压缩存储,以及生成PDF文档。源码完整开放,包含VCL窗体文件(如FrmFormatCells.dfm/pas)、BIFF协议解析模块(BIFF12_Recs5.pas等)、公式引擎(XLSEvaluate5.pa

pz8901234的博客 278

C++Builder 6 Update Pack 4:为遗留项目在现代系统上“续命”的必备补丁

在软件工程领域,维护历史遗留系统是一项常见且具有挑战性的任务,尤其当开发工具本身已停止官方支持时。其核心挑战在于确保老旧工具链能在现代操作系统上稳定运行,这直接关系到开发效率和软件质量。C++Builder 6(BCB6)作为一款经典的快速应用开发(RAD)工具,曾广泛应用于桌面数据库和工控软件。然而,其原始版本在Windows 10/11等现代系统上存在诸多兼容性和稳定性问题。Update Pack 4(UP4)作为官方最终累积补丁,通过修复编译器缺陷、增强VCL组件库的健壮性并修补安全漏洞,有效解决了I

423

Kiwi C/C++ API深度指南:StreamProvider灵活加载模型的进阶玩法

Kiwi C/C++ API 中最容易被忽视却又极具威力的功能,当属 **StreamProvider**——它让韩语形态分析器 Kiwi 摆脱"必须从磁盘目录加载模型"的限制,转而从内存、网络、数据库甚至加密资源中灵活读取模型文件。无论你是想把模型打进安装包做嵌入式部署,还是想动态下发模型实现热更新,本文都将带你从零掌握 StreamProvider 的核心用法与进阶玩法。 [![Kiwi韩语

gitblog_00587的博客 426

C++ RESTful服务实战:从环境搭建到Nginx部署完整指南

在现代软件开发中,构建高性能、可扩展的网络服务是核心需求。RESTful架构作为一种基于HTTP协议的软件架构风格,通过标准化的资源操作方式(如GET、POST、PUT、DELETE)和轻量的JSON数据格式,成为构建Web API的主流选择。其技术价值在于提供了清晰、无状态、可缓存的接口设计范式,极大提升了系统的可维护性和跨平台兼容性。在C++工程实践中,结合轻量级HTTP库(如cpp-httplib)和JSON处理库,开发者能够构建出媲美动态语言的高性能后端服务。应用场景广泛,从微服务架构中的独立模块到

weixin_30340819的博客 313

C++项目集成MinIO对象存储:从原理到实战的完整指南

对象存储是一种用于管理海量非结构化数据的核心云存储技术,它通过将数据作为对象(包含数据、元数据和唯一标识符)进行存储和访问,突破了传统文件系统在扩展性和分布式访问上的限制。其原理基于RESTful API(通常是S3兼容协议),将数据扁平化存储在可无限扩展的命名空间(桶)中,并通过HTTP/HTTPS进行读写。这项技术的核心价值在于提供了高可用、高扩展、低成本的数据存储方案,尤其适合处理图片、视频、日志等大规模数据。在应用场景上,对象存储广泛服务于数据湖、备份归档、静态资源托管和AI训练数据管理等领域。本文

weixin_33787529的博客 413
上一篇: 自己实现C++的线程池
下一篇: 官方的ORACLE9I在REDHAT9上安装的指南
老白
博客等级 码龄23年 59粉丝 106原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值