摘 要:利用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.