我可以从TThread的OnTerminate事件中引发异常吗? [英] Can I raise an exception from within OnTerminate event of a TThread?
问题描述
我编写了TThread
后代类,如果引发异常,则将异常的Class和Message保存在两个私有字段中
I wrote a TThread
descendant class that, if an exception is raised, saves exception's Class and Message in two private fields
private
//...
FExceptionClass: ExceptClass; // --> Class of Exception
FExceptionMessage: String;
//...
我认为我可以在OnTerminate
事件中raise
类似的异常,以便主线程可以处理它(这是简化版本):
I thought I could raise
a similar exception in the OnTerminate
event, so that the main thread could handle it (here is a simplified version):
procedure TMyThread.Execute;
begin
try
DoSomething;
raise Exception.Create('Thread Exception!!');
except
on E:Exception do
begin
FExceptionClass := ExceptClass(E.ClassType);
FExceptionMessage := E.Message;
end;
end;
end;
procedure TMyThread.DoOnTerminate(Sender: TObject);
begin
if Assigned(FExceptionClass) then
raise FExceptionClass.Create(FExceptionMessage);
end;
我希望发生标准的异常处理机制(错误对话框),
但是我得到的结果好坏参半:出现对话框,但随后出现系统错误,或者
或(更有趣)对话框出现,但调用线程的函数继续运行,就像从未引发异常一样.
我想问题出在调用堆栈上.
这是个坏主意吗?
还有另一种方法可以将线程异常与主线程分离,而以标准方式重现它们吗?
谢谢
I expect that the standard exception handling mechanism occurs (an error dialog box),
but I get mixed results: The dialog appears but is followed by a system error, or
or (more funny) the dialog appears but the function that called the thread goes on as if the exception were never raised.
I guess that the problem is about the call stack.
Is it a bad idea?
Is there another way to decouple thread exceptions from the main thread but reproducing them the standard way?
Thank you
推荐答案
在我看来,这个问题的根本问题是:
The fundamental issue in this question, to my mind is:
在线程的
OnTerminate
事件处理程序中引发异常时会发生什么.
What happens when you raise an exception in a thread's
OnTerminate
event handler.
通过调用Synchronize
在主线程上调用线程的OnTerminate
事件处理程序.现在,您的OnTerminate
事件处理程序正在引发异常.因此,我们需要弄清楚该异常如何传播.
A thread's OnTerminate
event handler is invoked on the main thread, by a call to Synchronize
. Now, your OnTerminate
event handler is raising an exception. So we need to work out how that exception propagates.
如果在OnTerminate
事件处理程序中检查调用堆栈,您将看到在CheckSynchronize
的主线程上调用了该堆栈.相关的代码是这样的:
If you examine the call stack in your OnTerminate
event handler you will see that it is called on the main thread from CheckSynchronize
. The code that is relevant is this:
try
SyncProc.SyncRec.FMethod; // this ultimately leads to your OnTerminate
except
SyncProc.SyncRec.FSynchronizeException := AcquireExceptionObject;
end;
因此,CheckSynchronize
捕获您的异常并将其存储在FSynchronizeException
中.然后继续执行,并且随后引发FSynchronizeException
.事实证明,在TThread.Synchronize
中引发了隐藏的异常. TThread.Synchronize
的最后死刑是:
So, CheckSynchronize
catches your exception and stashes it away in FSynchronizeException
. Excecution then continues, and FSynchronizeException
is later raised. And it turns out, that the stashed away exception is raised in TThread.Synchronize
. The last dying act of TThread.Synchronize
is:
if Assigned(ASyncRec.FSynchronizeException) then
raise ASyncRec.FSynchronizeException;
这意味着您试图在主线程中引发异常的尝试已被将其移回您的线程的框架阻止了.现在,这有点麻烦了,因为在这种情况下,在执行raise ASyncRec.FSynchronizeException
的时候,没有活动的异常处理程序.这意味着线程过程将引发SEH异常.那将使房子倒塌.
What this means is that your attempts to get the exception to be raised in the main thread have been thwarted by the framework which moved it back onto your thread. Now, this is something of a disaster because at the point at which raise ASyncRec.FSynchronizeException
is executed, in this scenario, there is no exception handler active. That means that the thread procedure will throw an SEH exception. And that will bring the house down.
因此,我从所有这一切得出的结论是以下规则:
So, my conclusion from all this is the following rule:
切勿在线程的OnTerminate
事件处理程序中引发异常.
Never raise an exception in a thread's OnTerminate
event handler.
您将不得不在主线程中找到另一种方式来显示此事件.例如,通过调用PostMessage
将消息排队到主线程.
You will have to find a different way to surface this event in your main thread. For example, queueing a message to the main thread, for example by a call to PostMessage
.
顺便说一句,您无需在Execute
方法中实现异常处理程序,因为TThread
已经实现了.
As an aside, you don't need to implement an exception handler in your Execute
method since TThread
already does so.
TThread
的实现将对Execute
的调用包装在try/except块中.这是在Classes
中的ThreadProc
函数中.相关代码为:
The implementation of TThread
wraps the call to Execute
in an try/except block. This is in the ThreadProc
function in Classes
. The pertinent code is:
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;
OnTerminate
事件处理程序是在捕获到异常之后调用的,因此您可以很好地选择从那里重新显示它,尽管如上所述我们不会天真地提高它.
The OnTerminate
event handler is called after the exception has been caught and so you could perfectly well elect to re-surface it from there, although not by naively raising it as we discovered above.
您的代码将如下所示:
procedure TMyThread.Execute;
begin
raise Exception.Create('Thread Exception!!');
end;
procedure TMyThread.DoOnTerminate(Sender: TObject);
begin
if Assigned(FatalException) and (FatalException is Exception) then
QueueExceptionToMainThread(Exception(FatalException).Message);
end;
为了清楚起见,QueueExceptionToMainThread
是您必须编写的一些功能!
And just to be clear, QueueExceptionToMainThread
is some functionality that you have to write!
这篇关于我可以从TThread的OnTerminate事件中引发异常吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!