【问题标题】:How to catch unhandled exception that are raised in background thread?如何捕获后台线程中引发的未处理异常?
【发布时间】:2018-09-18 15:16:25
【问题描述】:

我有执行匿名线程之类的习惯:

TThread.CreateAnonymousThread(
   procedure
   begin
     .....
   end).start;

但问题是,如果在执行过程中会引发一些未处理异常,那么我将不会收到警告!对于主线程,我们有Application.OnException。我们有类似的后台线程吗?

【问题讨论】:

    标签: delphi firemonkey


    【解决方案1】:

    TThread 有一个公共的FatalException 属性:

    如果Execute 方法引发了一个未在该方法中捕获和处理的异常,则线程终止并将FatalException 设置为该异常的异常对象。 应用程序可以从OnTerminate 事件处理程序中检查FatalException,以确定线程是否因异常而终止。

    例如:

    procedure TMyForm.DoSomething;
    begin
      ...
      thread := TThread.CreateAnonymousThread(...);
      thread.OnTerminate := ThreadTerminated;
      thread.Start;
      ...
    end;
    
    procedure TMyForm.ThreadTerminated(Sender: TObject);
    begin
      if TThread(Sender).FatalException <> nil then
      begin
       ...
      end;
    end;
    

    【讨论】:

    • 在使用 TThread 类时效果很好,但 OP 使用的是 CreateAnonymousThread,所以可能需要指出...
    • @JerryDodge 我知道 OP 正在使用。 CreateAnonymousThread()。它返回一个暂停的TThread 对象,您可以在调用Start() 之前调整其属性并为其分配事件处理程序
    • 从来不知道(FatalException 属性...)。不过,我想我还是更喜欢在原地捕捉和处理它们。一般来说,这似乎不太受控制。
    • @J... 这取决于您的需求。 Execute 包裹在 try..except 块中,OnTerminate 与 UI 线程同步。如果您不需要该同步,则可以跳过该事件
    • @RemyLebeau 谢谢!是的,它们是 FatalException 但无论如何更新所有现有线程以覆盖所有线程中的 ThreadTerminated 也比使用 try .. finally 在每个线程中更轻松:(
    【解决方案2】:

    N̶o̶,̶̶t̶h̶e̶r̶e̶̶i̶s̶̶n̶o̶t̶h̶i̶n̶g̶̶s̶i̶m̶i̶l̶a̶r̶̶f̶o̶r̶̶a̶̶b̶a̶c̶k̶g̶r̶o̶u̶n̶d̶̶t̶h̶r̶e̶a̶d̶.̶(谢谢,雷米!)

    最好的解决方案是始终绝对确保永远不会让异常从线程中逸出。理想情况下,您的线程过程应该如下所示:

    TThread.CreateAnonymousThread(
       procedure
       begin
         try
           { your code}
         except
           {on E : Exception do}
           {... handle it!}            
         end;
       end).start;
    

    【讨论】:

    • 其实有一点……看我的回答
    【解决方案3】:

    好的,我终于得到了最好的答案:

    将全局ExceptionAcquired 过程分配给我们自己的实现(默认为nil)。这个过程被调用来处理发生在除主线程之外的其他线程中的未处理异常。

    ExceptionAcquired := MyGlobalExceptionAcquiredHandler; 
    

    【讨论】:

      【解决方案4】:

      J... 和 Remy Lebeau 的回答对 delphi 提供的东西很好,但我需要更多,我最终决定对单元进行一些修改System.Classes

      var
        ApplicationHandleThreadException: procedure (Sender: TObject; E: Exception) of object = nil;
      
      function ThreadProc(const Thread: TThread): Integer;
        ...
        try
          Thread.Execute;
        except
          Thread.FFatalException := AcquireExceptionObject;
          if assigned(ApplicationHandleThreadException) and
             assigned(Thread.FFatalException) and
             (Thread.FFatalException is Exception) and
             (not (Thread.FFatalException is EAbort)) then
            ApplicationHandleThreadException(Thread, Exception(Thread.FFatalException));
        end; 
      

      这样你只需要分配ApplicationHandleThreadException 来处理任何TThread 中未处理的异常引发。您不必担心多线程,因为像ExceptAddr 这样的全局变量被声明为threadvar,所以一切正常,甚至检索堆栈跟踪!

      https://quality.embarcadero.com/browse/RSP-21269

      【讨论】:

      • 如果您希望您的代码在您自己以外的任何系统上编译时都可以工作,那么修改 RTL 源代码可能是个坏主意。
      • @J... 是的,我同意,尤其是当您升级 delphi 时!但是我无法想象任何使用没有接触 RTL 源的 firemonkey 的血统应用程序!
      猜你喜欢
      • 2011-05-20
      • 2012-09-05
      • 1970-01-01
      • 2020-01-25
      • 2013-03-09
      • 2017-03-11
      • 1970-01-01
      • 1970-01-01
      • 2014-03-19
      相关资源
      最近更新 更多