关于线程的Synchronize问题(100)

  • 主题发起人 主题发起人 帅义庭
  • 开始时间 开始时间

帅义庭

Unregistered / Unconfirmed
GUEST, unregistred user!
问题背景:主窗体中利用UDP控件接收其它设备发送来的数据包(每个报长度固定100字节),把数据包插入到数据缓冲区队列中并修改缓冲区队列的有关指针,//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//// 接收UDP数据包 ////~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//procedure TfrmMain.UDPServerUDPRead(Sender: TObject;
AData: TStream;
ABinding: TIdSocketHandle);var sz:INTEGER;
rbuf:ARRAY[0..100-1] of BYTE;
begin
AData.Read(rbuf,sz);
//读数据包 mQuene.InsertQuene( @mbuf[0],sizeof(mbuf) );
//把接收的数据包插入到缓冲区队列中end;
数据处理线程从数据缓冲区队列中取出并分析这些数据,线程取出数据后要修改缓冲区的指针;//*********************************// 任务执行程序 *//*********************************Procedure CJZTHREAD.Execute;
begin
Repeat Synchronize(DoTask);
//(1) //DOTask();
//(2) //TApplication(self).processMessages();
//(3) until(Terminated);
end;
我的问题: 数据缓冲区队列(mQuene)是主线程和数据处理线程(CJZTHREAD)的关键区域,如果在数据处理线程(CJZTHREAD)的DoTask的调用采用的是Synchronize(DoTask);那么无论是主线程还是数据处理线程(CJZTHREAD),在访问数据缓冲区队列(mQuene)时,都不需要在作任何线程间任何同步的操作,对吗? 相反如果在线程中直接调用DoTask()过程,则在访问公共数据缓冲区队列时必须使用一些同步的操作,是吗?在Procedure CJZTHREAD.Execute;中如果采用Synchronize(DoTask);
而把(3)删除则计算机启动程序运行时好像有一种死机的现象(主窗体上面的各种文字数据都非常非常慢才显示出来,如果把鼠标在WINDOWS 2000最下面一行的状态栏中点一下主窗体上的字可以立即显示出来);
 
如果在数据处理线程(CJZTHREAD)的DoTask的调用采用的是Synchronize(DoTask);那么无论是主线程还是数据处理线程(CJZTHREAD),在访问数据缓冲区队列(mQuene)时,都不需要在作任何线程间任何同步的操作,对吗? --1对(这时执行ToTask方法的实际上是主线程,工作线程会挂起直到此方法执行完)相反如果在线程中直接调用DoTask()过程,则在访问公共数据缓冲区队列时必须使用一些同步的操作,是吗?--2对(这时执行ToTask方法的是工作线程,那个公用数据对象需要用临界区保护)(3)是绝对不能加的,没任何理由要在工作线程中执行主线程消息循环中的代码。按上面2对那里说的做,程序界面就不会冻结了
 
szf,您好,谢谢您回答我的问题,另外还有点问题请教您,对公共数据缓冲区队列的操作,我采用的是类, 在类中定义了私有变量mMutex:THandle用于多个线程之间的同步,并且在该类中定义了2个方法Lock();和unLock();便于多个线程调用,我这样写程序的目的是为了程序好阅读好管理,不知道是否会带来什么其它的问题?书中介绍线程同步的时候都是采用的公共全局量.CONST QUENE_BUF_LEN=2048;
//队列缓冲区长度type TQuene = class(TObject) private mbuf:array[0..QUENE_BUF_LEN-1,0..TOTAL_CHL*GRIDS_PER_CHL-1] of INTEGER;
mCounter,pHeader,pRear:INTEGER;
mMutex :THandle;
public constructor Create();
//共享内存名称,队列中每个元素的长度,触发事件名称,访问互斥量名称 destructor Destroy();
override;
public function isEmpty():BOOLEAN;
function isFull() :BOOLEAN;
function GetQueneCount():INTEGER;
procedure InsertQuene(p:PBYTEARRAY;
n:integer);
procedure OutQuene(p:PBYTEARRAY;n:INTEGER);
procedure Empty();
procedure Lock();
procedure UnLock();
end;
VAR mQuene:TQuene;implementationconstructor TQuene.Create();
begin
inherited Create();
mMutex:=CreateMutex(nil,FALSE, PChar('QueneMutex'));
FillChar(mbuf[0],sizeof(mbuf),#0);
mCounter:=0;pHeader:=0;pRear:=0;
end;
destructor TQuene.Destroy();
begin
CloseHandle(mMutex);
inherited Destroy();
end;
function TQuene.isEmpty():BOOLEAN;
begin
if(mCounter<=0) then
result:=TRUE else
result:=FALSE;
end;
function TQuene.isFull(): BOOLEAN;
begin
;
if(mCounter>=QUENE_BUF_LEN) then
result:=TRUE else
result:=FALSE;
end;
function TQuene.GetQueneCount():INTEGER;
begin
result:=mCounter;
end;
procedure TQuene.InsertQuene(p:PBYTEARRAY;
n:integer);
begin
move( p[0],mbuf[pHeader,0],n );
pHeader:=(pHeader+1) mod QUENE_BUF_LEN;
INC(mCounter);
end;
procedure TQuene.OutQuene(p:PBYTEARRAY;n:INTEGER);
begin
move(mbuf[pRear,0],p[0],n);
pRear:=(pRear+1) mod QUENE_BUF_LEN;
mCounter:=mCounter-1;
if(mCounter<0) then
mCounter:=0;
end;
procedure TQuene.Empty();
begin
mCounter:=0;
pHeader:=0;
pRear:=0;
end;
procedure TQuene.Lock();
begin
WaitForSingleObject(mMutex,INFINITE);
end;
procedure TQuene.UnLock();
begin
ReleaseMutex(mMutex);
end;
initialization mQuene:=TQUENE.Create();finalization mQuene.Free();
 
没必要搞这么复杂吧,参考Classes单元的TThreadList类就行了。互斥 - 这种可命名的信号量是用在无法互相参考引用单元,但又要互相同步的情况,比如两个程序间...又比如这样写,代码少多了。。。unit Unit2;interfaceuses SyncObjs;type BYTEARRAY = array of Byte;
PBYTEARRAY = ^BYTEARRAY;
TMyThreadSafeBuf = class(TObject) private FBuffer: array of Integer;
FLock: TCriticalSection;
public constructor Create(MaxLen: Integer);
destructor Destroy;
override;
procedure InsertQuene(p:PBYTEARRAY;
n:integer);
procedure OutQuene(p:PBYTEARRAY;n:INTEGER);
end;
implementation{ TMyThreadSafeBuf }constructor TMyThreadSafeBuf.Create(MaxLen: Integer);
begin
FLock := TCriticalSection.Create;
SetLength(FBuffer, MaxLen);
end;
destructor TMyThreadSafeBuf.Destroy;
begin
FLock.Free();
inherited;
end;
procedure TMyThreadSafeBuf.InsertQuene(p: PBYTEARRAY;
n: integer);
begin
FLock.Enter();
try //受保护内存变量写操作 finally FLock.Leave();
end;
end;
procedure TMyThreadSafeBuf.OutQuene(p: PBYTEARRAY;
n: INTEGER);
begin
FLock.Enter();
try //受保护内存变量写操作 finally FLock.Leave();
end;
end;
end.
作为保护用的临界区,根本不需要外界知道的,什么lock,unlock都没必要暴露,只需提供的公用方法是“线程安全”至于其它资料上的同步信号量是全局公用,原因肯定是不想单元间关联太多,只要uses一个公用单元就行了。。。有必要时也需要这样做,但现在这个用例是没必要的。
 
szf,您好,您说的太好了,不需要暴露,在主窗体中的一个按钮事件程序,例如:procedure TfrmMain.Button1Click(Sender: TObject);
begin
mQuene.InsertQueneend;
它在访问公共变量时是否可以不需要使用 FLock.Enter();
FLock.Leave();这样的操作呢?在书上讲临界资源互斥时都是用的2个线程举例说明的,而我现在的情况是主进程中的一个按钮事件程序和一个数据处理线程之间访问公共变量,请问主进程中的那个按钮事件程序在访问公共变量时可否不使用FLock.Enter();
FLock.Leave();这样的操作? 谢谢szf!祝好!
 
使用临界区的原则是保护某个(些)变量不被两个线程同时更改。(只读没必要保护,除非读的结果影响接下来的修改操作)至于是两个工作线程,还是主线程,在这个原则前面是平等的。知道这个原则后,就不必举各种实例了,有些太具体的例子涉及的东西多了,都不知道怎么说才不会有语言逻辑上的错漏。
 
谢谢szf的解答
 
后退
顶部