您的位置:控制工程论坛网论坛 » 自动化软件 » 用Delphi实现多线程数据采集

马军成

马军成   |   当前状态:在线

总积分:13914  2024年可用积分:0

注册时间: 2006-12-01

最后登录时间: 2022-06-28

空间 发短消息加为好友

用Delphi实现多线程数据采集

马军成  发表于 2007/9/21 13:43:09      2570 查看 4 回复  [上一主题]  [下一主题]

手机阅读

用Delphi实现多线程数据采集

  转贴

用Delphi在工业控制和自动化实现多线程进行数据采集 
 
  数据采集技术在工业控制及自动化等领域中发挥着重要的作用。数据采集的一般过程是这样的:

①向采集卡发出通道选择指令。②选择要采集的通道号。③启动A/D转换。④等待,直到转换完成。⑤从采集卡读出数据。对于多通道的采集,在程序的设计中,一般采用的两种方法。查询法或中断法。所谓查询方法就是采用一个循环,依次采集各个数据通道。查询法的优点是程序简单,易于实现;缺点是采集过程中,CPU多数时间是在等待,造成资源的浪费。中断法是采用硬件中断的形式��先启动A/D转换,在转换结束时发出一中断信号��CPU响应采集卡的中断时读出所采集的数据。这样,在等待转换的时间里,CPU可以进行其他的计算工作,而不用处于等待状态。中断法的优点是资源能充分利用;但是程序设计复杂,尤其是当系统的硬件中断资源紧张时,很容易造成中断冲突;另外,在Windows或Win95等操作系统中,不允许用户安装中断处理程序时,则无法实现。 


---- 以上讨论的两种方法都是在DOS下的方法;在Win95下,现在有了一个更好的方法��多线程技术。现在,我们可以利用多线程技术来进行数据采集。 


---- 1. 采用多线程进行数据采集的优点 


---- Win95/98最让人喜爱的除了漂亮的界面以外,就是多线程与多任务了。DOS环境中,执行中的程序可以独占全部的资源;在Windows环境中,虽然它是一个略具雏形的多任务环境,但是只要你喜欢,你的程序仍然可以掌握所有的CPU时间。但是,在Windows95以及Windows NT中,一个程序无法独占所有的CPU执行时间。而且,一个程序也不是从头到尾一条线。相反,一个程序在执行中可以分为多个程序片段,同时执行。这些能同时执行的程序片段称为线程。在Windows 95以及Windows NT中,操作系统同一时间可以轮流执行多个程序,这就是多任务。 


---- 采用多线程进行数据采集可以有效地加快程序的反应速度、增加执行的效率。一般的程序中都要处理用户的输入,但用户的输入速度与CPU的执行速度相比就向走路与做飞机一样。这样,CPU就将浪费大量的时间用来等待用户的输入(如在DOS环境中)。如果采用多线程,那么就可以用一个线程等待用户的输入;另一个线程进行数据处理或其他的工作。对于数据采集程序,可以用一个单独的线程进行数据采集。这样,能最大限度的保证采集的实时性,而另外的线程同时又能及时地响应用户的操作或进行数据处理。否则,程序在采集数据时就不能响应用户的操作;在响应用户操作时就不能进行数据采集。尤其当采集的数据量很大,数据处理任务很重时,如果不采用多线程,采集时的漫长的等待是很让人接受的。 


---- 但是,多线程要比普通程序设计复杂得多。由于任一时刻都可能有多个线程同时执行,所以,许多的变量、数据都可能会被其他线程所修改。这就是多线程程序中最关键的线程间的同步控制问题。 


---- 2. 多线程进行数据采集应解决的问题 


---- 其实,多线程程序设计复杂是暂时的;如果,你采用传统的C进行多线程的设计,那么你必须自己控制线程间的同步。那将是很复杂的。但是,如果利用面向对象的设计方法,采用Delphi进行多线程程序设计,问题就简单多了。这是因为,Delphi已将多线程的复杂性替我们处理了,我们所要做的就是继承。 


---- 具体地说,多线程数据采集需要完成以下工作: 


---- ① 从TThread类派生一个自己的类SampleThread。这就是我们用于数据采集的类。进行采集时,只需要简单地创建一个SampleThread的实例。 


---- ② 重载超类TThread的Execute方法。在这一方法中将具体地执行数据采集任务。 


---- ③ 如果希望一边采集一边显示,就在编写几个用于显示采集进度的过程,供Execute方法调用。 


---- TThread类中最常用的属性/方法如下: 


Create方法:constructor Create

(CreateSuspended: Boolean);


---- 其中CreateSuspended参数确定线程在创建时是否立即执行。如果为True,新线程在创建后被挂起;如果为False,线程在创建后立即执行。 


FreeOnTerminate属性:

property FreeOnTerminate: Boolean;


---- 该属性确定程序员是否负责撤消该线程。如果该属性为True,VCL将在该线程终止时自动撤消线程对象。它的缺省值为False。 


OnTerminate属性:

property OnTerminate: TNotifyEvent;


---- 该属性指定一个当线程终止时发生的事件。 


---- 下面看一个具体的例子: 


---- 3. 多线程数据采集的实现 


---- 这是笔者开发的一个测抽油机功图的程序。它的功能是采集抽油机悬点的载荷及位移数据,经过处理后做出抽油机的功图。图1(略)所示是数据采集时的界面。点“采集数据”按钮后,程序将创建一新的线程,并设置其属性。这一新线程将完成数据采集任务。程序如下: 


Procedure TsampleForm.

DoSampleBtnClick(Sender: TObject);

Begin

ReDrawBtn.Enabled := True;

DoSampleBtn.Enabled := False;

FFTBtn.Enabled := True;

TheSampler := SampleThread.Create(False);

创建采集线程

TheSampler.OnTerminate := FFTBtnClick; 

采集完成后要执行的任务

TheSampler.FreeOnTerminate := True; 

采集完成后撤消

End;


---- 采集线程的类定义如下: 


Type

SampleThread = class(TThread)

Public

function AdRead(ach: byte): integer; safecall;

读A/D卡的函数

procedure UpdateCaption;

显示采集所用时间

private

{ Private declarations }

protected

thes, thep: real;

dt: real;

id: integer;

st, ed: LongInt;

procedure Execute; override;

这是关键。

End;


---- 在这个类中定义了一个函数AdRead用于操作A/D卡,两个过程用于显示采集的进度与所用时间。需要注意的是AdRead函数是用汇编写的,参数调用格式必须是safecall。 


---- 关键的重载方法Execute的代码如下: 


Procedure SampleThread.Execute;

Begin

StartTicker := GetTickCount; 

id := 0;

Repeat

thes := Adread(15) * ad2mv * mv2l;

采集第15通道

thep := Adread(3) * ad2mv * mv2n; 

采集第3通道

dt := GetTickCount - StartTicker;

sarray[id] := thes;

parray[id] := thep;

tarray[id] := dt;

inc(id);

Synchronize(UpdateCaption); 

注意:显示采集进度

Until id >=4096;

ed := GetTickCount;

Synchronize(ShowCostTime); 

注意:显示所用时间

end;


---- 从以上代码中可见,Execute与一般的代码并无本质区别。仅有的区别是显示采集进度和显示所用时间时,不能直接调用各自的过程,而是通过调用Synchronize间接地调用。这样作是为了保持进程间的同步。 


---- 4. 结论 


---- 以上的程序采用Delphi 4.0编程,在AMD-K6-2/300上实现。测试结果是这样的:采用多线程,采集4096个点一般耗用10~14s的时间;如果不采用多线程则需要1分钟到1分半。可见多线程可明显提高程序的执行效率。 
 

1楼 0 0 回复
  • 刘登军

    刘登军   |   当前状态:离线

    总积分:59  2024年可用积分:0

    注册时间: 2007-09-21

    最后登录时间: 2007-09-22

    空间 发短消息加为好友

    刘登军   发表于 2007/9/21 9:48:30

    我是想实现两个车轮同时找轮胎、同检测、同采集。所以是实现两轮同时检测,如果一轮在检测中有空闲的时间就另一轮开始检测,并才能同时完成!我是建立了两个线程一个轮一个,结果总是有死机的现象。你有什么高招?
    2楼 回复本楼

    引用 刘登军 2007/9/21 9:48:30 发表于2楼的内容

  • szzunzheng

    szzunzheng   |   当前状态:离线

    总积分:180  2024年可用积分:0

    注册时间: 2007-06-06

    最后登录时间: 2009-10-12

    空间 发短消息加为好友

    szzunzheng   发表于 2007/9/21 12:49:23

    3楼 回复本楼

    引用 szzunzheng 2007/9/21 12:49:23 发表于3楼的内容

  • 刘登军

    刘登军   |   当前状态:离线

    总积分:59  2024年可用积分:0

    注册时间: 2007-09-21

    最后登录时间: 2007-09-22

    空间 发短消息加为好友

    刘登军   发表于 2007/9/21 13:37:31

    我是写了一个左轮子的函数,一个右轮子的函数,并是同名,然后通过ynchronize(函数名)来调用,想实现一个轮子有空闲的时间,就另个轮子执行,最总实现同检测,但是我这样调用总是先执行一个轮子完了,才另一个执行,是什么原因?请教各位高手,给予解决?(我是用DELPHI写的)。
    4楼 回复本楼

    引用 刘登军 2007/9/21 13:37:31 发表于4楼的内容

  • 刘登军

    刘登军   |   当前状态:离线

    总积分:59  2024年可用积分:0

    注册时间: 2007-09-21

    最后登录时间: 2007-09-22

    空间 发短消息加为好友

    刘登军   发表于 2007/9/21 13:43:09

    马军成你好!


                 我看到,可以把你的QQ给发给我吗?我想请教你关两轮同检的问题,谢谢!我的QQ是121490825

    5楼 回复本楼

    引用 刘登军 2007/9/21 13:43:09 发表于5楼的内容

总共 , 当前 /