yan*_*kee 3 delphi delphi-7 tthread
我编写了一个TThread后代类,如果引发异常,则将异常的Class和Message保存在两个私有字段中
private
//...
FExceptionClass: ExceptClass; // --> Class of Exception
FExceptionMessage: String;
//...
Run Code Online (Sandbox Code Playgroud)
我以为我可以raise在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;
Run Code Online (Sandbox Code Playgroud)
我希望发生标准的异常处理机制(一个错误对话框),但我得到了混合的结果:对话框出现但后面是系统错误,或者(或者更有趣)对话框出现但是调用该线程的函数去了似乎从未提出异常.
我猜问题是关于调用堆栈.
这是个坏主意吗?
是否有另一种方法可以将线程异常与主线程分离,但以标准方式再现它们?
谢谢
在我看来,这个问题的根本问题是:
在线程的
OnTerminate事件处理程序中引发异常时会发生什么.
通过OnTerminate调用,在主线程上调用线程的事件处理程序Synchronize.现在,您的OnTerminate事件处理程序正在引发异常.所以我们需要弄清楚异常如何传播.
如果检查OnTerminate事件处理程序中的调用堆栈,您将看到它在主线程上被调用CheckSynchronize.相关的代码是这样的:
try
SyncProc.SyncRec.FMethod; // this ultimately leads to your OnTerminate
except
SyncProc.SyncRec.FSynchronizeException := AcquireExceptionObject;
end;
Run Code Online (Sandbox Code Playgroud)
所以,CheckSynchronize抓住你的例外并把它藏起来FSynchronizeException.然后继续执行,FSynchronizeException稍后提出.事实证明,这个被藏起来的例外被提出来了TThread.Synchronize.最后的垂死行为TThread.Synchronize是:
if Assigned(ASyncRec.FSynchronizeException) then
raise ASyncRec.FSynchronizeException;
Run Code Online (Sandbox Code Playgroud)
这意味着您在主线程中引发异常的尝试已被框架阻止,该框架将其移回到您的线程上.现在,这是一场灾难,因为在raise ASyncRec.FSynchronizeException执行的时候,在这种情况下,没有异常处理程序处于活动状态.这意味着线程过程将抛出SEH异常.这会让房子倒塌.
所以,我从这一切得出的结论是以下规则:
永远不要在线程的OnTerminate事件处理程序中引发异常.
您必须找到一种不同的方式来在主线程中显示此事件.例如,将消息排队到主线程,例如通过调用PostMessage.
顺便说一句,您不需要在Execute方法中实现异常处理程序,因为TThread已经这样做了.
实现TThread将调用包装Execute在try/except块中.这是在ThreadProc函数中Classes.相关代码是:
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;
Run Code Online (Sandbox Code Playgroud)
该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;
Run Code Online (Sandbox Code Playgroud)
而且要明确的QueueExceptionToMainThread是,您必须编写一些功能!
| 归档时间: |
|
| 查看次数: |
2119 次 |
| 最近记录: |