等待使用的TThread
对象是一种竞争条件,因为一旦对象的方法退出,就FreeOnTerminate=true
可以随时释放该对象。因此,除非您可以保证您在线程事件被触发之前调用,否则您不能保证有一个有效的对象可以读取其属性。基本上,一旦线程的构造函数退出,使用时所有的赌注都将关闭,除非您使用事件处理程序。用于“创建和忘记”类型的线程。如果您出于任何原因需要引用线程,如果您不小心,使用会变得很危险。Execute()
WaitForSingleObject()
OnTerminate
Handle
FreeOnTerminate=true
OnTerminate
FreeOnTerminate=true
FreeOnTerminate=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
或者当它尝试访问已经被释放的对象时,它可能会直接崩溃。EOSError
Handle
Handle
TThread