关于多线程的问题(200分)

  • 主题发起人 qiuqiu133
  • 开始时间
Q

qiuqiu133

Unregistered / Unconfirmed
GUEST, unregistred user!
问题1:利用多线程实现数据采集,大概意思是这样的:电脑上有多个串口,每个串口上有多个设备,每个串口为一个线程,这个线程所做的工作就是循环不间断的去采集线程所管串口上连接的设备的数据。请大家给出思路和源码。这个问题100分
问题2:在网上有一个利用多线程实现数据采集的源码,可是下载很麻烦,哪位能帮我下下来发到我邮箱:liuweiqiu505@163.com。网址为:http://www.programsalon.com/downloads24/sourcecode/windows/detail78310.html
这个问题100分
 
问题2已解决,只需回答问题1即可拿200分
 
楼主就按自已的思路做就可以了
我有个监控程序也是按这思路做的,连100多台设备都没问题,关键是看你线程中具体实现了
 
对于单核的CPU来说,做好同步是关键.-------感觉楼主的重点不在线程上,而是在每一个功能的具体实现上
 
每个串口一个spcomm,控件的数据接收是线程处理的。也可以把控件放入自创的一个线程里
 
不停的去读效率不高,可以用I/O事件模型来做。
 
有以下事实:
1.一般来说,串口读取的数据速率不会太高,很多设备采用9600波特率,即使采用57600也不必考虑毫秒级的应用
2.给设备发送读取的命令,设备要计算并返回结果,这也需要一些时间
所以设计基本按照楼主的想法就可以了。
1.每个串口开辟一条线程
2.每个线程维护一个设备数组
3.在线程的执行部分使用循环依次读取设备的数据
4.为了避免线程之间竞争引起极个别线程争夺不到资源的情况,在线程的执行部分,循环结束之后Sleep一下,我的经验是Sleep(100)就可以了。
我采用这样的方式成功实施过18个串口、每个串口连接10太设备,连续不中断运行200天的案例,
同步我采用的是临界区的方式,核心代码如下:
{ 执行线程解析队列中的命令 }
procedure TThreadCmdDispose.Execute;
var
tmpCommStatus: TCommStatus;
i: Integer;
begin
FreeOnTerminate := True;
Self.OnTerminate :=do
TerminateThread;
while not Terminateddo
begin
try
{ 是否暂停 }
if FPaused = True then
begin
FPaused := False;
FReceiving := False;
Self.Suspend;
end;
{ 是否接受数据 }
if FReceiving = True then
begin
try
{ 解析命令列表中的命令 }
DisposeCmd;
except
Sleep(0);
end;
end;
{ 防止过分争夺资源 }
Sleep(100);
except
Sleep(0);
end;
end;
end;
 
感谢楼上各位的回答,现在我自己写了一个线程,发现有如下问题:新创建一个线程后,给设备发送一次命令并且收到数据后,线程就报错,我根本监控不到错误出自何处。
我把所有代码都贴出来,希望高手能把我看看,我这个项目急,多谢大家了。
线程代码:
unit Unit_Thread;
interface
uses
Classes,Windows, Messages, SysUtils, Variants, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls,SPComm,StrUtils;
type //定义线程对象
TComThread = class(TThread)
private
protected
procedure Execute;override;
public
FGo:Boolean;
SComm:TComm;
//线程数带一个Comm控件
SendList:TStringList;//Comm控件的命令列表
Count:Integer;
//记录命令列表中已经发送到第几条命令了
ReceiveData:Array of Integer;
//Comm收到的数据所存放的数组
ReceiveDataCount:Integer;
//comm收到的数组的个数
procedure SendData(Data:String);//发送命令函数
procedure Initial(m:Integer);
//初始化数组
procedure CommReceiveData(Sender: TObject;
Buffer: Pointer;BufferLength: Word);
//串口收到的数据
constructor Create(CommN:String;BaudR:Integer;SendL:TStringList);
//创建线程函数
destructor Destroy;
//注销线程
end;

implementation
uses Unit_DataModule,Unit_Main;
procedure TComThread.Initial(m:Integer);
var
i:Integer;
begin
For i:=0 to mdo
ReceiveData:=0;
ReceiveDataCount:=0;
end;

procedure TComThread.SendData(Data:String);
var //发送命令数据
i:Integer;
btBuffer:Array of Byte;
begin
i:=0;
Data:=Trim(Data);
SetLength(btBuffer,100);
while Data<>''do
begin
btBuffer:=byte(StrToInt('$'+Data[1]+Data[2]));
Data:=Trim(MidStr(Data,3,Length(Data)));
i:=i+1;
end;
SComm.WriteCommData(@btBuffer[0],i);
end;

procedure TComThread.CommReceiveData(Sender: TObject;
Buffer: Pointer;
BufferLength: Word);
var
i:Integer;
r:array [0..127] of byte;
Reply:String;
begin
Reply:='';
For i:=0 to 127do
r:=0;
move(buffer^,r,BufferLength);
SetLength(ReceiveData,BufferLength);
Initial(BufferLength);
i:=0;
while (i<bufferLength)do
begin
ReceiveData[ReceiveDataCount]:=r;
ReceiveDataCount:=ReceiveDataCount+1;
Reply:=Reply+' '+IntToHex(r,2);
i:=i+1;
end;
application.ProcessMessages;
Sleep(500);
self.Resume;
end;

constructor TComThread.Create(CommN:String;BaudR:Integer;SendL:TStringList);
begin
FGo:=True;
SComm := TComm.Create(application);
SComm.BaudRate:=BaudR;
SComm.CommName:=CommN;
SComm.OnReceiveData:=CommReceiveData;
try
SComm.StartComm;
except
application.MessageBox(PChar(CommN+'被占据或不存在,无法建立与设备的通讯连接!'),'提示',MB_OK);
Exit;
end;
SendList:=TStringList.Create;
SendList:=SendL;
inherited Create(False);//False:线程一旦创立就运行 True:线程创立后先挂起
FreeOnTerminate := True;
end;

destructor TComThread.Destroy;
begin
inherited destroy;
end;

procedure TComThread.Execute;
var
DataStr:String;
begin
self.Count:=0;
while FGodo
begin
if (SendList<>Nil) and (SendList.Count>0) then
begin
DataStr:=SendList.Strings[Count];
SendData(DataStr);
if self.Count=SendList.Count-1 then
self.Count:=0
else
self.Count:=self.Count+1;
self.Suspend;
//发出一条命令后挂起,等待接收到命令后才重新唤醒
end;
end;
end;
end.

创建线程的代码:
var
ThreadArray:Array of TComThread;
ComCount:Integer
begin
ComCount:=0
SetLength(ThreadArray,10);//定义存放TComThread对象的数组长度为10
//下列的代码本身在一个循环里面,即创建多个TComThread
begin
SendL:=TStringList.Create;
SendL:=CreateSendList(Node);//CreateSendList函数的作用是生成Comm命令的TStringList
ComName:=Trim(Node.Text);//Comm的串口名字
ComBaudRate:=9600;//所有串口的波特率为9600
Com:=TComThread.Create(ComName,ComBaudRate,SendL);
ThreadArray[ComCount]:=Com;
ComCount:=ComCount+1;
end;
end;
 
1.Create里面的SendList:=TStringList.Create;这句去掉,既然是传参数进来就不必再创建对象了;
2.Execute里面的while FGodo
改成while not Terminateddo
,你的这句写成死循环了,这个线程就再也杀不掉了
3.Destroy去掉,线程一般用法都不需要手动释放,也不需要覆盖Destroy
4.给SendList加临界区
先这样改改试试吧
 
多谢muhx的回答
 
谢谢muhx
线程运行方面的问题基本上解决了。
但是出现了一个新问题,就是我把某线程所对应的Com口关闭,把该线程挂起,一段时间后又把该线程的com口打开,把线程恢复,可是有时候线程的Execute方法不再执行了。不知道是什么地方出问题了。
 
tstringlist在多线程里是不安全的,必须使用TThreadList
 
多人接受答案了。
 
顶部