我可以从TThread的OnTerminate事件中引发异常吗? [英] Can I raise an exception from within OnTerminate event of a TThread?

查看:102
本文介绍了我可以从TThread的OnTerminate事件中引发异常吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我编写了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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆