天下無雙
阿龍 --质量是流程决定的。

摘  要:利用C++Builder的多线程功能实现了工业控制过程中的实时数据处理,并以实例证明了它的简便性、有用性。

关键词:多线程;实时数据处理;C++Builder

0  引  言

在工业控制系统中,对数据的实时处理有着很高的要求,但在早期的工业控制系统中,常采用驻留程序(TSR)的设计来解决数据实时处理问题,这就对工控软件的开发者提出了较高的要求,而且该方法只能是基于DOS操作系统的,有着很大的局限性。随着多任务操作系统——WINDOWS的出现并在工控系统中广泛应用,数据实时处理的软件开发便不再成为一个难题了。

多任务操作系统将处理器的运行时间分成小的时间段,并分配给多个线程,每个线程在操作系统规定的时间段内运行。当线程使用完分配的时间段后,线程暂停执行;操作系统再将下一个时间段分配给其它线程执行;操作系统不断的将一个线程执行切换到另一个线程,经过一定时间的运行后,多个线程就同时完成了任务。由于各线程运行的时间段非常短,大约是20 ms,所以多线程能很好的满足工控中实时多任务处理的要求。

本文将以研华工控机对单片机传送来的数据的实时处理为例,介绍如何用多线程实现上位机对数据的实时处理。

1  开发软件的选用

笔者用Inprise公司的Borland CBuilder 40(以下简称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.

posted on 2007-09-18 10:14  阿龍  阅读(2424)  评论(0编辑  收藏  举报