线程的唤醒问题...(100分)

  • 主题发起人 主题发起人 logpie
  • 开始时间 开始时间
L

logpie

Unregistered / Unconfirmed
GUEST, unregistred user!
procedure TTestThread.Execute ;
begin
FLock.Enter ;
hEvent:=CreateEvent(nil,True,False,nil);
if intRun=1 then
SetEvent(hEvent) else
resetEvent(hEvent);
if WaitForSingleobject(hEvent,INFINITE)=WAIT_OBJECT_0 then
Synchronize(ShowMsg);
//ResetEvent(hEvent);
intRun:=0;
FLock.Leave ;
end;

procedure TTestThread.ShowMsg ;
begin
Form1.Memo1.Lines.Add(FData);
end;

intRun,hEvent均是TTestThread的私有成员
线程初始时处于不发信号的状态并加入线程池中
通过TTestThread(ThreadPooler.FActiveList.Items).intRun:=1;
来设置线程池内线程的发信号状态
问题1:照理该线程处于发信号状态时TTestThread.Execute因改继续往下走啊,也就是MEMO1上显示FDATA,而该段程序却并非如此,不知为何。
问题2:可以通过再次CREATE执行EXECUTE,但这样的效率很低,速度慢了很多,请问有什么办法可以不通过再次CREATE而执行EXECUTE(此时处于发信号状态),线程创建时是inherited Create(false), 所以也不能用resume.
谢谢各位了啊!
 
你可以在构造函数里CreateEvent(nil,False,False,nil);
然后在Execute 里while truedo
WaitFor****,同时给这个线程类增加一个办法Start;
procedure Start;
begin
if not FInWork //判断线程是否等待 then
SetEvent(FEvent) 就OK了
end;
 
张无忌说的对。
要实现线程的循环使用,就要在execute中设置循环。在循环中处理命令。
要线程工作的时候设置命令参数,然后setEvent;
循环中等待这个event 。读取命令,判断是结束循环,还是执行操作。
 
是这样吗:
procedure TTestThread.Execute ;
begin
Start;
while truedo
begin
FRes:=WaitForSingleObject(hEvent,INFINITE);
if FRes=WAIT_OBJECT_0 then
Synchronize(ShowMsg);
EndThread;
FInwork:=False;
end;
end;

procedure TTestThread.Start;
begin
if not FInwork then
FInwork:=true;
SetEvent(hEvent);
end;

procedure TTestThread.EndThread;
begin
if FInWork then
ResetEvent(hEvent);
FInWork:=False;
end;
 
好像速度有点慢呀
 
你的Execute 方法里只有Synchronize(ShowMsg)方法,这样来来看你根本就没有
必要用线程,你这个线程和直接在VCL界面里写代码没有什么两样,而且速度还要
比直接在VCL窗口里写代码的速度要慢.
 
抽空我给你一个框架代码。
 
晕~~SHOWMSG只是调试用的~
 
从你的TTestThread.Execute的代码来看,你的线程同时使用了临界区和事件对象来同步。而使用临界区的本意是保证一段代码只有一个在执行,当你通过TTestThread(ThreadPooler.FActiveList.Items).intRun:=1;设置时,这个线程可能处于等待最先执行的那个线程离开临界区呢。
我的建议:去掉FLock.Enter ;和FLock.Leave ;这两句。
 
楼主很厉害哦。一个线程用了三种同步方式:临界区同步,事件同步,主线程消息同步。大多数情况下只需要一种就够了。
因为要在线程中进行VCL的操作,所以假定必须用主线程消息同步吧(使用TThread的Synchronize方法)。
1.临界区代码段同步是为了解决多个线程访问相同的变量的冲突问题。本线程中不涉及这个问题,没有任何可能发生冲突的变量。算了,同意thx1180的说法,去掉。
2.事件同步一般是两个线程间使用的,单个线程如何使用事件,我无论如何想象不出来。换句话说,线程甲用来给事件发信号,线程乙用等待函数阻塞来捕获事件的信号状态。本线程一启动就发马上进入 WaitForSingleObject 函数,被阻塞住了,即便是你修改了intRun的值,线程也不可能倒回去运行将事件置为发信号状态。所以你的疑问一就有了答案。你必须在线程以外的地方来将事件置位。
3.后面你已经改好了,将事件置位的代码放到了另外一个方法中。这是个好主意。在TThread对象中,只有Execute中的代码以及由Execute调用到的代码是由线程执行的,需要考虑线程安全。除此之外都是在主线程环境下运行的。重复你从前的错误,你在Execute方法中调用了Start方法,是个错误。你应该在线程外调用Start方法以达到你的目的。
4.线程中作循环只是让线程能够无休止地继续运行。如果你不释放线程,下次仍然可以使用,不一定非得要重建的。大多数线程池都是这么设计的。一般来讲,如果线程的任务完成,线程就终止了。你的目的就达到了。但如果使用循环就必须有一种机制能够结束循环。你后面的代码看不出来你是如何结束循环的。从你上面列举的代码看,你可以使用以下三种方法结束线程:
a.等待函数不要用不限制等待,加一个超时值。终止线程的时候调用TThread的Terminate方法,在超时发生时有机会判断Terminated为True时退出线程。
b.换一个等待函数,改用MsgWaitForMultipleObjects,进入循环前用PeekMessage疏通线程消息管道。这样你可以将超值值设为无限长没问题。当你需要终止线程的时候给线程Post一个任意的消息,一旦捕获到消息,你有机会判断Terminated属性的值然后退出线程。
c.另外再专门建立一个事件,专为终止线程服务,你的等待函数将不得不改成WaitForMultipleObjects,同时等待两个事件。当事件一发生时还干以前的事,事件二发生时退出线程。这样你无需判断Terminated属性值。
归纳一下:干扰线程运行以达到同步目的大多数通过事件(Event)、信标(Semaphore)等内核对象,也可以配合这些对象引用线程消息。但是,内核对象具有较好的响应度,而消息会略为慢一些。但是象退出线程这样的操作,你并不需要太好的响应度,所以用消息比较合适。Delphi中大量的源码都是这样处理的。
 
谢谢各位了,好久遇不到大富翁这么热情了:)
Synchronize是用来和VCL同步的
在这个例子里Event实质是用来挂起唤醒线程的,而不是用来几个线程同步的
我想您大概说的是CreateMetux吧
而这里flock是用来保证输出顺序的(为了测试用的,呵呵),去不去其实无所谓
事实上正确答案正像张无忌和xwings说的那样,应该在execute中不停的循环来捕获信号状态。结束循环我是使用用resetevent再次挂起,当然这时循环也不执行了,需再次进入循环时便调用Start
当然您说的也是好方法,我也准备用PeekMessage+PostThreadMessage来做结束线程
正在测试中,过会结分,再次谢谢大家了!
 
把执行放在一个While(TRUE)循环中
 
另外,谁再给讲讲MsgWaitForMultipleObjects
[:D]
 
可以参见我写的线程代码,稍稍修改一下就可以实现接受命令,处理,通知主线程等功能。
http://www.delphibbs.com/keylife/iblog_show.asp?xid=1193
 
多人接受答案了。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
2K
DelphiTeacher的专栏
D
后退
顶部