| 用C++Builder建立多线程COM服务器 | |||
| 一、线程、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行的:
就可以了。重新编译你的程序,同时开两个客户试一试,是不是并发执行了? 先别高兴得太早,如果你同时开了五个客户,并且其中四个在执行费时的访问,你就会发现第五个客户的访问要等待一段时间。这种现象与C++Builder的实现代码有关。 作了前面的修改后,服务器启动后会预先生成几个线程,这些线程各自进入一个STA中。当服务器接到客户的访问要求后,会循环指定一个线程负责这个客户的建立COM对象、访问COM对象的事务。 比如第一个客户要求建立一个COM对象,服务器就给一号线程发消息,让这个线程建立一个COM对象并把这个COM对象的接口传给客户,以后第一个客户对这个COM对象的访问就全由一号线程代理。而第二个客户的建立COM对象、访问COM对象的事务就由服务器指定二号线程来办,如果客户太多,线程用完了,服务器又会让一号线程负责客户的要求,依次循环。如果客户很多,线程可能会负责几个客户的访问要求,而由同一个线程服务的客户的访问就会顺序执行。预先生成的线程数缺省为系统的CPU个数乘以四,也就是四个(除非你的机器有好几个CPU)。 只能同时服务四个客户当然是不行的,让我们继续修改。打开主CPP文件,可以看到下面两行代码:
看到那个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对象:
注意,这段代码必须写在TThread::Execute()中,因为只有TThread::Execute()里的代码才是真正运行在新线程中的。另外决不能调用pComObj->Release()。 后记 学COM的念头起于看李维写的那三本书中的第一本的时候,李维描述了建立多线程服务器的重要性,但具体方法只是一笔带过。后来我看了Delphi带的例子,想用在C++Builder中,却无从下手。在关于COM的部分,Delphi和C++Builder相差太大了,而又没有这方面的C++Builder的书,网上的资料也很少,只好自己摸索。期间曾在网上发贴提问,总是没人回答,痛苦啊! |
相关推荐
C++Builder图像处理实战:从VCL组件到算法优化与OpenCV集成
图像处理作为计算机视觉的基础,其核心在于对像素数据的数学运算与空间变换,以实现平滑、锐化、边缘检测等效果。在Windows桌面应用开发中,选择合适的工具链能极大提升开发效率与性能平衡。C++Builder凭借其成熟的VCL组件库,为图像处理提供了TBitmap与TCanvas等原生支持,开发者可直接操作ScanLine进行高性能像素遍历,实现均值滤波、中值滤波等经典算法。通过TParallel::For进行多线程优化,并结合OpenCV集成,能够将传统图像处理与现代计算机视觉库无缝衔接,满足工业质检、医学影
C++ builder 多线程实例(生产-消费)
根据网上生产消费模型改写的易于理解(自己)的线程例子。
C++ Builder 6 源代码深度解析:从经典VCL框架到现代工程实践
在桌面应用开发领域,快速应用开发(RAD)框架通过可视化组件和事件驱动编程,极大提升了软件构建效率。其核心原理在于将界面设计与业务逻辑分离,通过封装底层系统API(如Windows消息机制和图形设备接口)为可重用的控件库。这种范式不仅加速了原型开发,更在企业级应用、工控系统和数据库前端等场景中证明了其工程价值。以经典的VCL框架为例,它代表了RAD理念在C++环境下的高峰实践。本文聚焦于对C++ Builder 6(BCB6)这一特定历史版本所附带的示例源代码进行体系化研究,旨在挖掘其中关于**数据库访问层
c++ builder 多线程
c++ builder 多线程方法和实例,
C++ Builder 6实战教程:从零构建Windows桌面应用,掌握C++与VCL核心开发
C++作为一门经典的面向对象编程语言,其核心价值在于对内存管理、指针操作和底层系统调用的精细控制,这使得它在需要高性能和硬件直接交互的桌面应用开发中依然占据重要地位。其技术原理通过类、继承、多态等机制,实现了代码的模块化和复用,为构建复杂软件系统提供了坚实基础。在工程实践中,快速应用程序开发工具能够显著提升GUI程序的开发效率,通过可视化拖拽和事件驱动编程模型,将界面设计与业务逻辑紧密结合。C++ Builder 6正是这样一款经典的RAD工具,它基于VCL组件库,让开发者能够在直观的IDE环境中,快速构建
C++Builder实战:从源码到精通,手把手构建桌面应用
在桌面应用开发领域,快速应用开发(RAD)是一种通过可视化工具和组件库高效构建软件的方法论。其核心原理在于将界面设计、业务逻辑与底层代码解耦,通过事件驱动模型和组件复用,大幅提升开发效率。这种模式的技术价值在于让开发者能聚焦业务实现,而非底层细节,尤其适合需要快速迭代的Windows平台工具、数据库管理系统和企业内部应用。C++Builder作为经典的RAD工具,结合了C++的性能优势与VCL组件的丰富性,是学习这一开发范式的理想平台。通过源码驱动的实战学习,开发者可以深入理解从数据库操作到界面绑定的完整流
在BCB中使用多线程 ( 转载 )
多线程编程是提高系统资源利用率的一种常见方式。它占用的资源更小,启动更快,还可以实现在后台运行一些需时较长的操作。一、初识TThread对象VCL提供了用于多线程编程的TThread类,在这个类中封装了Windows关于线程机制的Windows API,通常将它的实例成为线程对象。线程对象通过封装简化了多线程应用程序的编写。注意,线程对象不允许控制线程堆栈的大小或安全属性。若需要控制这 些,必须使
C++Builder 6 核心架构与实战开发指南:从VCL框架到数据库应用
快速应用程序开发(RAD)是一种通过可视化工具和组件库提升软件构建效率的经典范式,其核心原理在于将界面设计与业务逻辑分离,通过事件驱动模型简化开发流程。这一理念在桌面应用开发中具有重要技术价值,尤其适用于需要快速构建图形用户界面(GUI)的工业控制、数据采集和管理信息系统等场景。C++Builder 6作为该范式的代表性工具,其内置的VCL(可视化组件库)框架和高效的C++编译器,使得开发者能够通过拖拽组件和编写事件处理代码,快速实现功能完备的应用程序。本文聚焦于C++Builder 6的**VCL框架**
C++Builder 2009深度解析:VCL框架、数据库开发与多线程编程实战技巧
在Windows桌面应用开发领域,快速应用开发(RAD)理念通过可视化组件和事件驱动编程,显著提升了软件构建效率。其核心原理在于将界面元素、数据绑定和业务逻辑进行封装,通过属性-事件模型实现高效交互。这一技术价值在于能够以更少的代码量快速构建功能复杂、界面专业的应用程序,特别适合维护遗留系统或开发特定行业的工具软件。应用场景广泛覆盖工控、数据采集、仪器仪表等需要稳定高效Windows桌面解决方案的领域。本文聚焦于C++Builder 2009这一经典RAD工具,深入探讨其VCL框架的精髓使用心法,详解TCl
C++ 操作 Word 文档 2 大方案对比:COM自动化 vs 第三方库(Aspose.Words)
本文深入对比了C++操作Word文档的两种主流方案:COM自动化和第三方库(如Aspose.Words)。COM自动化通过Microsoft Office提供完整功能但依赖性强,而Aspose.Words则支持无依赖部署和跨平台操作。文章详细分析了两者在功能特性、性能指标、部署要求及成本方面的差异,并提供了实战场景建议,帮助开发者根据项目需求做出最优选择。
Borland C++ Builder 6.0:经典RAD工具在现代环境下的配置与实战指南
快速应用程序开发(RAD)是一种通过可视化组件和模型驱动方法提升软件构建效率的经典范式,其核心原理在于将重复性界面编码转化为拖拽式设计,从而让开发者聚焦业务逻辑实现。在技术价值层面,RAD工具显著降低了GUI应用程序的开发门槛,尤其适合需要快速迭代的内部工具和工业控制软件。虽然现代跨平台框架已成主流,但在维护遗留系统、教学演示或需要轻量级原生执行的特定场景中,基于VCL(Visual Component Library)的经典RAD环境仍具独特优势。本文以Borland C++ Builder 6.0为例,
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
C++Builder 6 Update Pack 4:为遗留项目在现代系统上“续命”的必备补丁
在软件工程领域,维护历史遗留系统是一项常见且具有挑战性的任务,尤其当开发工具本身已停止官方支持时。其核心挑战在于确保老旧工具链能在现代操作系统上稳定运行,这直接关系到开发效率和软件质量。C++Builder 6(BCB6)作为一款经典的快速应用开发(RAD)工具,曾广泛应用于桌面数据库和工控软件。然而,其原始版本在Windows 10/11等现代系统上存在诸多兼容性和稳定性问题。Update Pack 4(UP4)作为官方最终累积补丁,通过修复编译器缺陷、增强VCL组件库的健壮性并修补安全漏洞,有效解决了I
Kiwi C/C++ API深度指南:StreamProvider灵活加载模型的进阶玩法
Kiwi C/C++ API 中最容易被忽视却又极具威力的功能,当属 **StreamProvider**——它让韩语形态分析器 Kiwi 摆脱"必须从磁盘目录加载模型"的限制,转而从内存、网络、数据库甚至加密资源中灵活读取模型文件。无论你是想把模型打进安装包做嵌入式部署,还是想动态下发模型实现热更新,本文都将带你从零掌握 StreamProvider 的核心用法与进阶玩法。 [


被折叠的 条评论
为什么被折叠?



