[英]Waiting for a TThread to finish with WaitForSingleObject()
I am creating a thread, and then say want to wait for it to terminate with a WFSO call (simplified pseudo-code below, obviously no one wants to wait on a thread right after creating it).我正在创建一个线程,然后说要等待它通过 WFSO 调用终止(下面是简化的伪代码,显然没有人想在创建线程后立即等待它)。
constructor TFileScannerThread.Create(Parameters)
begin
/// assign parameters to private variables, and call
inherited Create(true); //Create Suspended
FreeOnTerminate := true;
Start;
end;
In the main thread在主线程中
fst := TFileScannerThread.Create(BaseFolder, ScanMode, AbortEvent);
/// I presume, the next call would block the main thread until the child thread is done
/// but, it seems to create a deadlock & WFSO never returns
/// I get a WAIT_TIMEOUT if I use a finite wait time
WaitForsingleObject(fst.Handle, INFINITE);
What am I doing wrong/missing?我在做什么错/错过了什么? If I do not have the WFSO the thread completes in about 10 seconds.
如果我没有 WFSO,线程将在大约 10 秒内完成。
Edit: Creating with FreeOnTerminate=false does not create this issue.编辑:使用 FreeOnTerminate=false 创建不会产生此问题。
Waiting on a TThread
object that uses FreeOnTerminate=true
is a race condition, as the object could be freed at any moment once its Execute()
method has exited.在使用
FreeOnTerminate=true
的TThread
object 上等待是一种竞争条件,因为一旦它的Execute()
方法退出,object 可以随时被释放。 So unless you can guarantee that you are calling WaitForSingleObject()
before the thread's OnTerminate
event is fired, then you are not guaranteed to have a valid object on which to read its Handle
property.因此,除非您可以保证在触发线程的
OnTerminate
事件之前调用WaitForSingleObject()
,否则不能保证您有一个有效的 object 可以读取其Handle
属性。 Basically, once the thread's constructor exits, all bets are off when using FreeOnTerminate=true
, unless you use an OnTerminate
event handler.基本上,一旦线程的构造函数退出,使用
FreeOnTerminate=true
时所有的赌注都将关闭,除非您使用OnTerminate
事件处理程序。 FreeOnTerminate=true
is meant for "create and forget" kind of threads. FreeOnTerminate=true
用于“创建并忘记”类型的线程。 If you need to refer to a thread for any reason, using FreeOnTerminate=true
becomes dangerous if you are not very careful.如果您出于任何原因需要引用线程,如果您不小心,使用
FreeOnTerminate=true
会变得很危险。
If you are going to wait on a TThread
object, there is no point on using FreeOnTerminate=true
on that object, since you can just free the object yourself once the wait is complete.如果您要在
TThread
object 上等待,则在该 object 上使用FreeOnTerminate=true
是没有意义的,因为您可以在等待完成后自己释放 object。 Doing so ensures the object remains valid until you manually free it, thus you can use its Handle
at any time:这样做可以确保 object 在您手动释放它之前保持有效,因此您可以随时使用它的
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;
(you don't need to call Start()
inside the constructor, use CreateSuspended=False
instead, the thread will not actually begin running until after the constructor exits) (您不需要在构造函数中调用
Start()
,而是使用CreateSuspended=False
,直到构造函数退出后,线程才会真正开始运行)
That being said, it is impossible for WaitForSingleObject()
to return WAIT_TIMEOUT
on an INFINITE
timeout.话虽如此,
WaitForSingleObject()
不可能在INFINITE
超时时返回WAIT_TIMEOUT
。 But it is possible for it to return WAIT_FAILED
, such as when the TThread
object is freed before WaitForSingleObject()
is called, or even while it is still waiting on the Handle
, thus closing the Handle
while it is being used.但是它有可能返回
WAIT_FAILED
,例如在调用WaitForSingleObject()
之前释放TThread
object 时,或者即使它仍在等待Handle
,因此在使用Handle
时关闭它。
With regard to a deadlock, if you have an OnTerminate
event handler assigned to the TThread
object, that handler gets called via the TThread::Synchronize()
method so that the handler runs in the main thread.关于死锁,如果您将
OnTerminate
事件处理程序分配给TThread
object,则该处理程序将通过TThread::Synchronize()
方法调用,以便处理程序在主线程中运行。 But, if the main thread is blocked on WaitForSingleObject()
, then it can't service any TThread::Synchronize()
(or TThread::Queue()
) requests.但是,如果主线程在
WaitForSingleObject()
上被阻塞,那么它不能为任何TThread::Synchronize()
(或TThread::Queue()
)请求提供服务。 Thus, you end up with the worker thread waiting on the main thread while the main thread is waiting on the worker thread - deadlock.因此,您最终会导致工作线程在主线程上等待而主线程在工作线程上等待 - 死锁。
To avoid that, you can call WaitForSingleObject()
in a loop with a short timeout so that you can call the RTL's CheckSynchronize()
function periodically while you are waiting:为避免这种情况,您可以在循环中调用
WaitForSingleObject()
并设置一个短暂的超时,以便在等待时定期调用 RTL 的CheckSynchronize()
function:
var h: THandle;
h := fst.Handle;
while WaitForSingleObject(h, 500) = WAIT_TIMEOUT do
CheckSynchronize;
fst.Free;
There are other issues to deal with when using a blocking wait, like SendMessage()
calls from other threads to the main thread.使用阻塞等待时还有其他问题需要处理,例如从其他线程到主线程的
SendMessage()
调用。 So you would need to service those requests as well:因此,您还需要为这些请求提供服务:
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;
Alternatively, add the Classes.SyncEvent
handle to the wait as well ( TThread::Synchronize()
and TThread::Queue()
signal it internally when there are requests pending):或者,也将
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;
FYI, TThread
has its own WaitFor()
method that performs a blocking wait on the thread to terminate, while servicing TThread::Synchronize()
/ TThread::Queue()
and SendMessage()
requests, similar to above:仅供参考,
TThread
有自己的WaitFor()
方法,该方法在线程上执行阻塞等待以终止,同时为TThread::Synchronize()
/ TThread::Queue()
和SendMessage()
请求提供服务,类似于上面:
fst := TFileScannerThread.Create(BaseFolder, ScanMode, AbortEvent);
...
fst.WaitFor;
fst.Free;
Just note that calling TThread::WaitFor()
on a TThread
object that uses FreeOnTerminate=true
is not safe, either.请注意,在使用
FreeOnTerminate=true
的TThread
object 上调用TThread::WaitFor()
也不安全。 It will either fail with an EThread
or EOSError
exception when the Handle
is closed between internal loop iterations, or it will likely just crash outright when it tries to access the Handle
of a TThread
object that has already been freed.当
Handle
在内部循环迭代之间关闭时,它将失败并出现EThread
或EOSError
异常,或者当它尝试访问已释放的TThread
object 的Handle
时,它可能会直接崩溃。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.