等待使用的TThread对象是一种竞争条件,因为一旦对象的方法退出,就FreeOnTerminate=true可以随时释放该对象。因此,除非您可以保证您在线程事件被触发之前调用,否则您不能保证有一个有效的对象可以读取其属性。基本上,一旦线程的构造函数退出,使用时所有的赌注都将关闭,除非您使用事件处理程序。用于“创建和忘记”类型的线程。如果您出于任何原因需要引用线程,如果您不小心,使用会变得很危险。Execute()WaitForSingleObject() OnTerminateHandleFreeOnTerminate=trueOnTerminateFreeOnTerminate=trueFreeOnTerminate=true
如果您要等待一个TThread对象,那么在该对象上使用是没有意义的FreeOnTerminate=true,因为一旦等待完成,您就可以自己释放该对象。这样做可以确保对象在您手动释放它之前保持有效,因此您可以随时使用它Handle:
constructor TFileScannerThread.Create(Parameters)
begin
inherited Create(False);
FreeOnTerminate := false;
// assign parameters to private variables
end;
fst := TFileScannerThread.Create(BaseFolder, ScanMode, AbortEvent);
...
WaitForSingleObject(fst.Handle, INFINITE);
fst.Free;
(您不需要Start()在构造函数内部调用,CreateSuspended=False而是使用,线程不会真正开始运行,直到构造函数退出后)
话虽如此,超时WaitForSingleObject()返回是不可能WAIT_TIMEOUT的。INFINITE但是它有可能返回WAIT_FAILED,例如当TThread对象在被调用之前被释放时WaitForSingleObject(),或者即使它仍在等待,从而在它被使用时Handle关闭。Handle
关于死锁,如果您为对象OnTerminate分配了一个事件处理程序TThread,则该处理程序将通过该TThread::Synchronize()方法调用,以便该处理程序在主线程中运行。但是,如果主线程在 上被阻塞WaitForSingleObject(),那么它就不能服务任何TThread::Synchronize()(或TThread::Queue())请求。因此,您最终会导致工作线程在主线程上等待而主线程在工作线程上等待 - 死锁。
为避免这种情况,您可以WaitForSingleObject()在一个短超时的循环中调用,这样您就可以CheckSynchronize()在等待时定期调用 RTL 的函数:
var h: THandle;
h := fst.Handle;
while WaitForSingleObject(h, 500) = WAIT_TIMEOUT do
CheckSynchronize;
fst.Free;
使用阻塞等待时还有其他问题需要处理,例如SendMessage()从其他线程调用主线程。因此,您还需要为这些请求提供服务:
var
h: THandle;
ret: DWORD;
msg: TMsg;
h := fst.Handle;
repeat
case MsgWaitForMultipleObjects(1, h, False, 1000, QS_SENDMESSAGE) of
WAIT_OBJECT_0, WAIT_FAILED: Break;
WAIT_OBJECT_0 + 1: PeekMessage(msg, 0, 0, 0, PM_NOREMOVE);
WAIT_TIMEOUT: CheckSynchronize;
end;
until False;
fst.Free;
或者,也将Classes.SyncEvent句柄添加到等待中(TThread::Synchronize()并TThread::Queue()在有待处理请求时在内部发出信号):
var
h: array[0..1] of THandle;
ret: DWORD;
msg: TMsg;
h[0] := fst.Handle;
h[1] := SyncEvent;
repeat
case MsgWaitForMultipleObjects(2, h, False, INFINITE, QS_SENDMESSAGE) of
WAIT_OBJECT_0, WAIT_FAILED: Break;
WAIT_OBJECT_0 + 1: CheckSynchronize;
WAIT_OBJECT_0 + 2: PeekMessage(msg, 0, 0, 0, PM_NOREMOVE);
end;
until False;
fst.Free;
仅供参考,TThread有它自己的WaitFor()方法在线程上执行阻塞等待以终止,同时为TThread::Synchronize()/TThread::Queue()和SendMessage()请求提供服务,类似于上面:
fst := TFileScannerThread.Create(BaseFolder, ScanMode, AbortEvent);
...
fst.WaitFor;
fst.Free;
请注意,调用TThread::WaitFor()使用的TThread对象FreeOnTerminate=true也不安全。当内部循环迭代之间关闭时,它会失败并出现异常,EThread或者当它尝试访问已经被释放的对象时,它可能会直接崩溃。EOSErrorHandleHandleTThread