gpt4 book ai didi

delphi - 我可以在 TThread 的 OnTerminate 事件中引发异常吗?

转载 作者:行者123 更新时间:2023-12-03 15:41:01 31 4
gpt4 key购买 nike

我编写了一个 TThread 后代类,如果引发异常,则将异常的 Class 和 Message 保存在两个私有(private)字段中

private
//...
FExceptionClass: ExceptClass; // --> Class of Exception
FExceptionMessage: String;
//...

我认为我可以在 OnTerminate 事件中引发类似的异常,以便主线程可以处理它(这是一个简化版本):

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;

我期望发生标准异常处理机制(错误对话框),但我得到的结果好坏参半:出现对话框,但随后出现系统错误,或者或者(更有趣)出现对话框,但调用线程的函数继续运行,就好像从未引发异常一样。
我猜问题出在调用堆栈上。
这是一个坏主意吗?
是否有另一种方法可以将线程异常与主线程分离,但以标准方式重现它们?
谢谢

最佳答案

在我看来,这个问题的根本问题是:

What happens when you raise an exception in a thread's OnTerminate event handler.

通过调用 Synchronize 在主线程上调用线程的 OnTerminate 事件处理程序。现在,您的 OnTerminate 事件处理程序正在引发异常。因此我们需要弄清楚异常是如何传播的。

如果您检查 OnTerminate 事件处理程序中的调用堆栈,您将看到它是在主线程上从 CheckSynchronize 调用的。相关代码是这样的:

try
SyncProc.SyncRec.FMethod; // this ultimately leads to your OnTerminate
except
SyncProc.SyncRec.FSynchronizeException := AcquireExceptionObject;
end;

因此,CheckSynchronize 捕获您的异常并将其隐藏在 FSynchronizeException 中。然后继续执行,并随后引发FSynchronizeException。事实证明,隐藏的异常是在 TThread.Synchronize 中引发的。 TThread.Synchronize 的最后一个死亡行为是:

if Assigned(ASyncRec.FSynchronizeException) then 
raise ASyncRec.FSynchronizeException;

这意味着您尝试在主线程中引发异常的尝试已被框架阻止,该框架将其移回您的线程。现在,这是一场灾难,因为在执行 raise ASyncRec.FSynchronizeException 时,在这种情况下,没有事件的异常处理程序。这意味着线程过程将抛出SEH异常。这会让房子垮掉。

因此,我的结论是以下规则:

      切勿在线程的 OnTerminate 事件处理程序中引发异常。

您必须找到一种不同的方式在主线程中显示此事件。例如,通过调用 PostMessage 将消息排队到主线程。

<小时/>

顺便说一句,您不需要在 Execute 方法中实现异常处理程序,因为 TThread 已经这样做了。

TThread 的实现将对 Execute 的调用包装在 try/except block 中。这是在ClassesThreadProc 函数中。相关代码为:

try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;

OnTerminate 事件处理程序在捕获异常后被调用,因此您完全可以选择从那里重新显示它,尽管不是像我们上面发现的那样天真地引发它。

您的代码将如下所示:

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 是您必须编写的一些功能!

关于delphi - 我可以在 TThread 的 OnTerminate 事件中引发异常吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17940747/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com