摘 要:利用C++Builder的多线程功能实现了工业控制过程中的实时数据处理,并以实例证明了它的简便性、有用性。 关键词:多线程;实时数据处理;C++Builder 0 引 言 在工业控制系统中,对数据的实时处理有着很高的要求,但在早期的工业控制系统中,常采用驻留程序(TSR)的设计来解决数据实时处理问题,这就对工控软件的开发者提出了较高的要求,而且该方法只能是基于DOS操作系统的,有着很大的局限性。随着多任务操作系统——WINDOWS的出现并在工控系统中广泛应用,数据实时处理的软件开发便不再成为一个难题了。 多任务操作系统将处理器的运行时间分成小的时间段,并分配给多个线程,每个线程在操作系统规定的时间段内运行。当线程使用完分配的时间段后,线程暂停执行;操作系统再将下一个时间段分配给其它线程执行;操作系统不断的将一个线程执行切换到另一个线程,经过一定时间的运行后,多个线程就同时完成了任务。由于各线程运行的时间段非常短,大约是20 ms,所以多线程能很好的满足工控中实时多任务处理的要求。 本文将以研华工控机对单片机传送来的数据的实时处理为例,介绍如何用多线程实现上位机对数据的实时处理。 1 开发软件的选用 笔者用Inprise公司的Borland CBuilder 40(以下简称CB)作为开发工具。CB是Inprise公司的新一代面向对象、可视化的快速应用程序开发环境(RAD),它是C开发工具的自然发展。与其它的RAD工具相比,CB以C代码的高效、简洁保证了数据处理的灵活性、实时性。 2 应用实例 2.1 构件组成及关键属性 在CB的集成开发环境下,从可视化构件库(VCL)中选用若干构件组成如下图所示的窗体。 图1 实例的窗体构件图 图示构件的关键属性设置如下: 构件 | 属性 | 取值 | DataSourcel | DataSet | Tablel | Tablel | DatabaseName(数据库别名) | ty | Tablel | TableName(表名) | Datacollect.db | Tablel | Active | True | MSComml | Rtheshold | 6 | DBGridl | DatsAource | Datasourcel |
其中MSComml 是Microsoft公司开发的用于串行通信的ACTIVEX控件,通过对控件有关属性的设置,并对On_Comm事件进行编程,它将自动完成对串行通信过程的控制,由于本例只是简单的数据接受处理,所以只需设置RTheshold属性(当发生comEvReceive通信事件并引发On_Comm事件之前接受缓冲区中的最小字符数,缺省设置为0,不引发On_Comm事件),其它属性默认即可。 在该程序中采用两个线程分别进行串行数据的接收与显示,接收线程为TDataCollection 类,显示线程为TDataDisplay类,都是派生于CB提供的TThread类。TThread类封装了使用WindowsAPI函数创建线程、挂起线程、终止线程、控制线程同步的所有复杂的内部细节,并提供了方法、属性、事件给软件开发者控制线程。 2.2 TThread类的简介 ·FreeOnTerminate属性: 设置该属性为true,线程结束时,线程对象自动销毁,反之程序员必须在线程结束时显示的将线程销毁。 ·Handle属性: 线程句柄,利用这个属性,Windows的API函数可对线程进行操作。 ·Priority属性: 线程优先级。Windows是抢先多任务的,系统根据优先级来调度线程,具体定义可查联机帮助。 ·ReturnValue属性: 线程执行后的返回值,根据它来判断线程是否成功运行。 ·Suspended属性: 设置它为rtue时,线程将挂起;反之,线程执行。 ·Terminated属性: 终止线程执行。 ·Execute方法: 这是派生类必须重载的方法。这个函数就是新线程的执行代码。 ·Synchronize方法: 同步方法。把对于VCL访问置于该函数体内。这个成员函数使所有对VCL的访问由主VCL线程来调用,避免了多线程的冲突。新线程执行进入这个函数体时,调用转入主VCL线程,新线程挂起。 ·Terminate方法: 终止线程的执行。 ·WaitFor方法: 等待线程的终止,并返回ReturnValue属性的值。 ·OnTerminate事件: 线程终止时触发的响应事件。 实际应用中,TThread类对象常与TEvent类对象(事件对象)配对使用,Tevent类在多线程的通信中起着信号灯的作用,能有效的保护共享资源。 2.3 本例中两个线程的部分代码 ·TDatacollection线程:// fastcall TDataCollection::TDataCollection(bool CreateSuspended) :TThread(CreateSuspended) { FreeOnTerminate=true;//设置线程终止时自动清除标志} // voidfastcall TDataCollection::Execute()//重载函数 //Place thread code here while(Forml→DisplayDataOkEvent→WaitFor(1000)=wrSignaled); { if(Terminated)//检查线程的退出标志,如果为true,则退出 break; Forml→DisplayDataOkEvent→ResetEvent();//数据显示事件对象复位 Synchronize(Collection);//数据采集 Forml→ReadDataOkEvent→SetEvent(); //设置数据采集事件对象为有信号状态用于通知数 //据显示线程可以开始数据显示操作 } } // voidfastcall TDataCollection::Collection() { //TODO:Add your source code here Forml→MSComml→OnComm;//执行已设计的通信过程 } // voidfastcall TForml::MSComm(TObject*Sender)//OnComm事件的执行 //函数执行体 {OleVariant data;//设置转换变量 switch(MSComml→CommEvent)//根据CommEvent属性的不同值采取响应 //处理措施 { case 2: data=MSComml→Input;//读取缓冲区的返回字符串 Disply=data.ChangeType(0×0100);//把字符串的类型由 //MSCmm的string转为CB的AnsiString,并赋值 //给全局变量Disply case N://其它通信事件或通信错误 …………… default:ShowMessage(“no response!”);//没有通信事件发生而显示的信息 } } ·TDataDisplay线程: // fastcall TDataDisplay::TDataDisplay(bool CreateSuspended) :TThread(CreateSuspended) { FreeOnTerminate=true; } // voidfastcall TDataDisplay::Exerute() { //Place thread code here while(Forml→ReadDataOkEvent→WaitFor(1000)=wrSignaled); { if (Termi nated) break; Forml→ReadDataOkEvent→ResetEvent();//数据读事件对象复位 Synchronize(Display);//数据显示 Forml→DisplayDataOkEvent→SetEvent()://设置数据显示事件对象 //为有信号状态用于通知数 //据读线程可以开始数据采集操作 } } // voidfastcall TDataDisplay::Display() { //TODO:Add your source code here Forml→Tablel→AppendRecord(ARRAYOFCONST(Time(),Disply)); //把采集到的数据与此时的时间加入数据库的表中,成为表的新记录 Forml→DBGridl→Update();//显示该记录 } 在主线程中对线程初始化后,该程序运行如下: 图2 线程初始化后窗体图 3 结束语 本文介绍了多线程的一些内容与如何在CB中实现它的方法。CB作为可视化开发语言,它的开发界面友好、开发难度小、开发周期短,并且又具有C语言的简洁、高效性,在项目开发中确是一个较好的选择。 参考文献: [1] 汤庸,苏军根,C Builder3.0编程方法与范例[M].北京:海潮出版社,1990. [2] 王士元.C高级实例程序设计[M].北京:清华大学出版社,1996. [3] Joe Campbell.串行通信C程序员指南[M].北京:清华大学出版社,1995. |