这个问题看似微不足道,但我希望你不要忽视它
在销毁TThread对象之前,通常需要等到调用TThread.Execute()方法的线程完成,因为只有这样我们才能确定,例如,不再访问在类的析构函数中销毁的对象。因此,有必要调用Terminate来设置线程必须检查的Terminated标志,以确定是否退出,然后调用WaitFor()方法。
因为线程可能被挂起,我认为在调用WaitFor之前恢复它是好的,否则调用线程将会死锁。并且因为线程可以多次挂起,所以应该恢复相同的次数,对吗?
while Suspended do
Resume;
如果线程被创建暂停,我们不必担心当我们恢复线程只会终止它时会调用TThread.Execute()方法 - 它不会(如果我错了请纠正我) )。
我所说的建议为每个被释放的TThread对象使用以下代码行:
MyThread.Terminate;
while MyThread.Suspended do
MyThread.Resume;
MyThread.WaitFor;
MyThread.Free;
不幸的是,当我们销毁创建多个线程的应用程序时,为每个被破坏的TThread对象编写这样一段代码会导致代码很长甚至不透明。
因此我得出的结论是,所有这些都可以放在TThread类的重写析构函数中,这样就可以调用MyThread.Free(如果设置了MyThread.FreeOnTerminate,则调用MyThread.Terminate),而不关心被破坏的对象是否是TThread对象:
destructor TMyThread.Destroy;
begin
//if FreeOnTerminate, the calling thread cannot wait for itself
if GetCurrentThreadId <> ThreadId then
begin
Terminate;
while Suspended do
Resume;
WaitFor;
end;
{free all objects created in this class}
inherited Destroy;
end;
请原谅我提出这样一个基本问题。但是,我希望通过这种方式了解您的意见 - 我希望这是一种普遍的方式 - 销毁TThread对象。我问这个问题,因为我从我的同事的代码中学到了他们通常使用第一个代码示例来销毁这些对象,但他们从来没有用来检查等待的线程是否没有被挂起,如果线程我认为有点危险可能会在代码中的某处暂停。因此,我试图找到一种破坏这个类的对象的通用方法,这将使代码更清晰,更安全。我希望我没有让情况变得更糟 - 你觉得怎么样?
提前感谢您的建议。
答案 0 :(得分:8)
你的建议已经在TThread.Destroy析构函数中执行了很多,并且调用TMyThread.free会完成你的建议。要清除线程类拥有的任何对象,可以在OnTerminate事件中执行该操作,该事件将作为线程关闭逻辑的一部分进行调用。
答案 1 :(得分:8)
没有通用的方法来阻止线程,就像没有通用的方法(优雅地)停止进程一样。每一个都不同。
对于某些线程,通过Terminated
方法设置其Terminate
属性就足够了。但是,其他线程调用GetMessage
或MsgWaitForMultipleObjects
之类的函数,这些函数将阻塞直到发生某些事情,例如消息到达或内核句柄变为信号。 TThread.Terminate
无法使这些事情发生,因此无法使这些线程停止运行。当我写这些线程时,我提供了自己的函数来通知它们停止运行。我可能会调用PostThreadMessage
强制将消息强制到线程的队列中,或者我可能会发出线程类已经提供的事件,以通知它终止请求。
不要担心恢复挂起的线程。无论如何你真的不应该暂停它们。挂起线程的唯一安全方法是让线程自行挂起,一旦你有了,你可以保证至少有两个线程控制线程运行的时间:线程本身暂停它,至少还有一个其他线程线程再次恢复它。线程应该控制自己的执行。
如果TThread.Terminate
是虚拟的,那就太好了。然后每个线程类都可以提供一种自定义方式来通知自己它应该停止运行。有些人可以设置Terminated
,其他人可以发布消息,信号事件或做任何他们需要的事情。但是,非虚拟方法对于花费大量时间等待其他事情的线程不能很好地工作。当前方式仅适用于能够经常轮询其Terminated
属性的线程。
某些线程设置了FreeOnTerminate
属性。对于那些线程,您的代码不安全。从技术上讲,在这些对象上调用任何方法是不安全的,因为线程可以随时终止。但即使您知道线程仍在运行且线程对象仍然存在,该对象肯定会在调用Terminate
后的某个时间停止存在。你不能在一个免费终止的线程对象上调用WaitFor
,你肯定无法调用Free
。