请问怎么终止进入死循环的线程?用什么代替线程中的sleep函数? (200分)

  • 主题发起人 主题发起人 majorsoft
  • 开始时间 开始时间
M

majorsoft

Unregistered / Unconfirmed
GUEST, unregistred user!
我有这样的一个线程类
TThrLoop=class(TThread);
private
fItemNo:integer;
public
procedure playItem;
procedure execute;
override;
destructor destroy;override;
end;

procedure ThrLoop.execute;
begin
while fItemNo<5do
begin
Synchronize(playitem);
sleep(2000);//等待2秒在执行下一个playItem;//1
if fItemNo=-1 then
exit;
//2
inc(fItemNo);

if (fItemNo=5) then
fItemNo:=0;

end;

destructor ThrLoopPly.destroy;
begin
fItemNo:=-1;
inherited destroy;
end;

ThrLoop:TThrLoop;
..
用ThrLoop.free不能立即终止线程,大概要等1~2秒种。1,2不能互换,互换的话,永远终止不了线程。
用API函数又不太安全,请高手指点。
另外线程中使用sleep函数并不精确,用什么方法代替好?
 
我是进来学的.帮你顶顶.
 
线程用法错误,QQ里谈
 
对线程的概念理解有问题
TThread.Destroy中会调用WaitFor
所以会等待
只有Execute函数正常执行完,free才会正常退出
其实整个线程类中,只有Execute是真正在线程中的部分
其它都是在主线程中
DESTROY是主线程执行的,WaitFor就是等待EXECUTE退出
所以你的做法就是错误的
不要OVERRIDE DESTROY
改用Terminate
将While的条件中加入Not Terminated
如果Terminated为true,则EXECUTE必须退出
 
TThrLoop=class(TThread);
private
fItemNo:integer;
public
procedure playItem;
procedure execute;
override;
constructor create...;
end;

procedure ThrLoop.execute;
begin
while not terminateddo
begin
Synchronize(playitem);
// 将等待两秒放到playitem里做
inc(fItemNo);

if (fItemNo=5) then
fItemNo:=0;

end;

constructor ThrLoopPly.create...;
begin
...
FreeOnTerminate := true;
end;

最后要说明的是:你在EXECUTE里的事都是在PLAYITEM里做的,而PLAYITEM实际上是由主线程做的,所以你的线程实际上毫无用处。
 
2003-10-31 09:30:07 猛禽
对线程的概念理解有问题
2003-10-31 09:30:18 majorsoft
free 调用destroy.
我在destroy中 fItemNo:=-1
2003-10-31 09:30:28 majorsoft
我觉得也是。

2003-10-31 09:30:39 猛禽
TThread.Destroy中会调用WaitFor
2003-10-31 09:30:47 猛禽
所以会等待
2003-10-31 09:30:49 majorsoft

2003-10-31 09:31:06 猛禽
只有Execute函数正常执行完,free才会正常退出
2003-10-31 09:31:18 majorsoft
waitfor是不是等待线程被调度执行?
2003-10-31 09:31:26 猛禽
其实整个线程类中,只有Execute是真正在线程中的部分
2003-10-31 09:31:32 猛禽
其它都是在主线程中
2003-10-31 09:31:34 majorsoft


2003-10-31 09:31:54 猛禽
DESTROY是主线程执行的,WaitFor就是等待EXECUTE退出
2003-10-31 09:32:00 猛禽
所以你的做法就是错误的
2003-10-31 09:32:11 猛禽
不要OVERRIDE DESTROY
2003-10-31 09:32:15 猛禽
改用Terminate
2003-10-31 09:32:37 猛禽
将While的条件中加入Not Terminated
2003-10-31 09:32:55 独心(小伍)
我只是觉得ThrLoopPly.destroy没有调用
原来这样
2003-10-31 09:32:55 猛禽
如果Terminated为true,则EXECUTE必须退出
2003-10-31 09:33:11 majorsoft
我试过了,好象不行。
我再试一次
2003-10-31 09:33:28 猛禽
你是怎么试的,肯定还有错误,把代码贴到DFW上
2003-10-31 09:34:33 majorsoft
等等
2003-10-31 09:35:15 流铭
不过,当 FreeOnTerminate 为 True 时,还是由线程自己 Free 线程对象的
2003-10-31 09:36:00 majorsoft
自己可以销毁自己吗?
2003-10-31 09:36:23 猛禽
那也是先terminate再free
2003-10-31 09:36:30 majorsoft
对。

2003-10-31 09:36:41 猛禽
可以
2003-10-31 09:36:46 猛禽
当然可以
2003-10-31 09:36:46 majorsoft
其实在destroy中有terminate
2003-10-31 09:37:02 独心(小伍)
while fItemNo<5 and not terminateddo

2003-10-31 09:37:13 majorsoft
只用free 就可以了。

2003-10-31 09:37:18 猛禽
对,如小伍这样
2003-10-31 09:37:25 猛禽
最好用FREEONTERMINATE
2003-10-31 09:37:43 流铭
不是“自己可以销毁自己”
线程 和 Thread 线程对象 是两码事
2003-10-31 09:37:50 猛禽
这样当你调用TERMINATE后,OBJECT会在EXECUTE执行完后调用FREE
2003-10-31 09:38:03 猛禽
流铭说的对
2003-10-31 09:38:11 猛禽
线程只是EXECUTE部分
2003-10-31 09:38:24 猛禽
而TTHREAD对象是主线程中的一个对象
2003-10-31 09:38:59 独心(小伍)
procedure ThrLoop.execute;
里加上freeonterminate:=ture
2003-10-31 09:39:06 majorsoft
你们试过可以吗?》?

2003-10-31 09:39:32 majorsoft
开始我都是按你们说的那样做的,但就是终止不了
2003-10-31 09:39:33 猛禽
FREEONTERMINATE最好在CREATE的时候设置
2003-10-31 09:39:48 猛禽
不可能,你把完整代码贴出来
2003-10-31 09:40:33 流铭
主线程和分线程都能够操纵 TTHREAD 对象
2003-10-31 09:41:33 majorsoft
我想贴但上不了DFW
2003-10-31 09:42:31 猛禽
所以操作TTHREAD对象时要注意同步互斥的问题
2003-10-31 09:42:43 独心(小伍)
发 到qq 上我帮你贴上
2003-10-31 09:44:36 猛禽
看我在DFW上的跟贴
2003-10-31 09:46:56 majorsoft
对,你那样确实可以退出,但要等sleep()执行完。有没有办法直接退出?
2003-10-31 09:47:32 独心(小伍)
那要api 了
2003-10-31 09:47:36 猛禽
那你就要在SLEEP前判断Terminate
2003-10-31 09:47:51 猛禽
不要用API,如果要用API,就不要用TTHREAD类
2003-10-31 09:48:17 猛禽
二者有冲突,可能导致MEM LEAK或CRASH
2003-10-31 09:48:28 独心(小伍)
是不是很容易抛出异常?
2003-10-31 09:49:01 majorsoft
24287564(猛禽) 09:47:36
那你就要在SLEEP前判断Terminate
那样没有用,
2003-10-31 09:56:15 猛禽
你去看我在DFW上贴的代码
2003-10-31 09:56:30 猛禽
SLEEP不要放在线程里,放到PLAYITEM里去
2003-10-31 09:57:51 majorsoft
那么主线程不sleep了?
2003-10-31 09:59:55 majorsoft
问题就在sleep上面。
waitfor 要等execute执行完,但execute在执行sleep(2000)
2003-10-31 10:01:21 majorsoft
我用的方法和你们推荐的方法都可以终止线程,但是要等sleep执行完。
现在的问题是,我需要马上终止它
2003-10-31 10:01:21 majorsoft
我用的方法和你们推荐的方法都可以终止线程,但是要等sleep执行完。
现在的问题是,我需要马上终止它
2003-10-31 10:03:53 猛禽
你的马上是什么概念?
2003-10-31 10:04:18 猛禽
就是主线程想什么时候停它就能立即停下?
2003-10-31 10:05:58 猛禽
那还有两个办法:
一是不要用TTHREAD,用API
二是不要用SLEEP(因为假设PLAYITEM执行时间可以忽略,则大部分时间是用于SLEEP,那样的话,平均需要等等待SLEEP时间的一半),改用其它方法,如SUSPEND等
2003-10-31 10:07:49 majorsoft
24287564(猛禽) 10:04:18
就是主线程想什么时候停它就能立即停下?

2003-10-31 10:08:42 majorsoft
用suspend的话,怎么控制时间?

2003-10-31 10:09:24 猛禽
另外一个线程
2003-10-31 10:09:50 猛禽
但是如果PLAYITEM的执行时间较长的话,这种方法也不行,只能用API
2003-10-31 10:10:35 majorsoft
这个时间可以不计
2003-10-31 10:11:14 猛禽
那可以用那种方法
2003-10-31 10:11:38 猛禽
PLAYITEM后启动一个计时线程
2003-10-31 10:11:48 猛禽
然后把自己SUSPEND
2003-10-31 10:11:59 猛禽
计时到把它RESUME
2003-10-31 10:12:22 猛禽
主线程要停时把两个线程都TERMINATE即可
2003-10-31 10:14:06 majorsoft
那么终止计时线程还是要等待啊
2003-10-31 10:15:37 majorsoft
用API函数的话,可以,但知道不安全。到底怎么做比较安全呀?

2003-10-31 10:15:54 猛禽
计时线程不能用SLEEP
2003-10-31 10:16:15 猛禽
API也可以安全,但用API就不能用TTHREAD类,会比较麻烦
2003-10-31 10:18:59 majorsoft
24287564(猛禽) 10:15:54
计时线程不能用SLEEP 那用什么计时?
2003-10-31 10:19:17 majorsoft
不太好用API那个。

2003-10-31 10:22:34 猛禽
循环GetTickcount
2003-10-31 10:22:41 猛禽
或用Timer
2003-10-31 10:23:41 majorsoft
怎么在线程中用timer控制?
2003-10-31 10:24:15 猛禽
建立消息循环,用API创建TIMER
2003-10-31 10:25:34 majorsoft
有点头晕,
2003-10-31 10:35:37 majorsoft
你能帮我写一下吗?
2003-10-31 10:43:36 猛禽
倒,没空:)
2003-10-31 10:46:40 majorsoft
:(
那算了,
现在我用API倒可以终止。
但我连续创建线程,然后用API终止它,内存会增加,
2003-10-31 10:47:12 majorsoft
速度是以k为单位的。

2003-10-31 10:47:42 猛禽
因为用API终于不会FREE TTHREAD类,所以出现MEM LEAK
2003-10-31 10:47:45 majorsoft
但我想那个线程对象比较小,不会是它消耗的内存呀。
2003-10-31 10:48:04 猛禽
而如果手工FREE则会因为THREAD已经被API终止而出现EXCEPTION
2003-10-31 10:48:30 majorsoft
》》而如果手工FREE则会因为THREAD已经被API终止而出现EXCEPTION

2003-10-31 10:48:44 majorsoft
但我觉得那线程对象比较小啊。

2003-10-31 10:49:10 majorsoft
只有几个数据成员,也不至于造成这么大的泄露呀。
2003-10-31 10:49:33 猛禽
呵呵,你创建的部分是不大,DELPHI建了些什么就不知道了:)
2003-10-31 10:50:09 majorsoft
有没有办法在线程终止了,然后再释放内存的?

2003-10-31 10:50:21 majorsoft
我用freeMem怎么样?
2003-10-31 10:54:19 猛禽
也不行,如果用API,最好的办法就是不要用TTHREAD或自己写一个TTHREAD
2003-10-31 10:57:57 majorsoft
哎!
 
对于“最后要说明的是:你在EXECUTE里的事都是在PLAYITEM里做的,而PLAYITEM实际上是由主线程做的,所以你的线程实际上毫无用处。”
说的对,
我原来的意思是用线程来sleep(varTime),来避免在主线程中sleep。
varTime指等待的时间,
 
问题初步解决了,感谢猛禽!
使用GetTickCount来代替sleep(2000),
TThrTimer=class(TThread)
private
fInterval:cardinal;
fThr:TThread;
public
constructor create(CreateSuspended: Boolean;Thr:TThread;interval:cardinal);
procedure execute;override;
end;

TThrLoopPly=class(TThread)
private
fItemNo:integer;
fPlyItems:TPlyItems;
fcBox:TComboBox;
thrTimer:TThrTimer;

...
public
procedure playItem;
procedure execute;
override;
destructor Destroy;override;
constructor create...
end;

{ TThrTimer }
constructor TThrTimer.create(CreateSuspended:Boolean;
Thr:TThread;
interval: cardinal);
begin
inherited create(CreateSuspended);
fThr:=Thr;
fInterVal:= interval;
freeOnTerminate:=true;//注意要写上,不然的话会MEM Leak;
end;

procedure TThrTimer.execute;
var
n:cardinal;
begin
n:=getTickCount+fInterval;
while (not terminated) and (GetTickCount<n)do
sleep(1);//注意sleep(1);
fThr.Resume;//延迟时间到,唤醒ThrLoopPly.
end;

constructor TThrLoopPly.create(CreateSuspended: Boolean;
plyItems: TplyItems;
cbox: TComboBox);
begin
inherited create(CreateSuspended);
fPlyItems:=plyItems;
fcBox:=cbox;
freeOnterminate:=true;
end;

destructor TThrLoopPly.Destroy;
begin
inherited;
thrTimer.Terminate;//最好放在这里
end;

var ThrLoopPly:=TThrLoopPly
...
ThrLoopPly:=TThrLoopPly.create....
....
ThrLoopPly.terminate;//而不使用free;把freeOnTerminate设为True,当调用Terminate之后,自动free.
procedure TThrLoopPly.execute;
var
n:Cardinal;
begin
if (length(fplyItems)=0) then
exit;
while (fItemNO<length(fplyItems)) and (not Terminated)do
begin
Synchronize(PlayItem);
ThrTimer:=TThrTimer.create(false,self,fplyItems.fDelay);
Suspend;
inc(fItemNo);
if fItemNO=length(fplyItems) then
fItemNo:=0;
end;
end;

procedure TThrLoopPly.playItem;
begin
fcBox.ItemIndex:=fItemNo;
frm_gendis_Watch.cb_itemNoChange(nil);
end;

上面的程序均调试通过!
如果大家有更好的办法,请不吝赐教!
 
type
TThrLoopPly = class(TThread)
private
FEvent: Integer;
protected
procedure Execute;
override;
public
procedure TerminateSelf;
end;

procedure TThrLoopPly.execute;
var
n:Cardinal;
begin
FreeOnTerminate := True;
FEvent := CreateEvent(nil, false, false, nil);
while not Terminateddo
begin
waitforsingleobject(FEvent, 2000);
// 等待2秒
if terminated then
break;
Synchronize(PlayItem);
// 你的代码在这
end;
closehandle(FEvent);
end;

procedure TThrLoopPly.TerminateSelf;
begin
Terminate;
try
SetEvent(FEvent);
except
end;
end;
 
好办法和Another_eYes说的差不多
做一个全局的QuitEvent,所有的子线程都检查该事件,如果有该事件,就退出线程。
如果要在子线程莉等待,用
if waitforsingleobject(FEvent, 2000) = WAIT_OBJECT_0 then
退出子线程;
// 等待2秒
 
Try it :
while not Terminateddo

begin
......
end;

or:
thrloop1:thrloop;
procedure ThrLoop.execute;
begin
ThrLoop1.freeOnTerminate:=true;
........
end;

 
单单就你想缩短等待时间,可以这样:
修改
sleep(2000);//等待2秒在执行下一个playItem;//1
为:
i:=200;
repeat
i:=i-1;
sleep(10);
until (i<1)or(Terminated);
 
要同步用TEvent啦,很简单的.
也可以这样,你在Thread里设置一个标志,如果要退出,把那个标志置True,并且把thread.FreeOnTerminate也设true,这样就行啦,sleep完就检查那个标志,如果是true就直接退出.
 
多人接受答案了。
 
后退
顶部