代码之家  ›  专栏  ›  技术社区  ›  luntain

如何防止后台线程中的异常终止应用程序?

  •  42
  • luntain  · 技术社区  · 17 年前

    我可以联系 AppDomain.CurrentDomain.UnhandledException 记录后台线程的异常,但如何防止它们终止运行时?

    5 回复  |  直到 12 年前
        1
  •  44
  •   Frederik Struck-Schøning    8 年前

    首先,你真的应该尽量不要在后台线程中抛出异常,也不要处理异常。如果您控制了委托的运行方式,请将其封装在try-catch块中,并想出一种方法将异常信息传递回主线程(如果您显式调用BeginInvoke,则使用EndInvoke,或者在某处更新某些共享状态)。

    忽略未处理的异常可能是危险的。如果你有一个真正的不可处理的异常(OutOfMemoryException出现在脑海中),你无论如何都做不了多少,你的进程基本上注定要失败。

    回到。Net 1.1中,背景线程中未经处理的异常将被抛出到任何地方,主线程将很乐意继续运行。这可能会产生严重的影响。所以在。Net 2.0改变了这种行为。

    现在,在非主线程中抛出的未经处理的异常将终止进程。您可能会收到此通知(通过订阅AppDomain上的事件),但该进程仍将死亡。

    由于这可能很不方便(当你不知道线程中会运行什么,也不确定它是否得到了适当的保护,并且你的主线程必须具有弹性时),有一个解决方法。它旨在作为传统设置(这意味着,强烈建议您确保没有杂散线程),但您可以通过以下方式强制执行前一种行为:

    只需将此设置添加到您的服务/应用程序/任何配置文件中:

    <configuration>
      <runtime>
        <!-- the following setting prevents the host from closing when an unhandled exception is thrown -->
        <legacyUnhandledExceptionPolicy enabled="1" />
      </runtime>
    </configuration>
    

    它似乎不适用于ASP。NET。

    有关详细信息(以及CLR即将推出的版本可能不支持此设置的严重警告),请参阅 http://msdn.microsoft.com/en-us/library/ms228965.aspx

        2
  •  10
  •   Mitch Wheat    17 年前

    来自Joe Albahari的优秀作品 threading 文章:

    这个。NET框架提供了一个 全局异常的低级事件 处理: AppDomain。未处理的异常。这 当出现未处理的事件时,事件会触发 任何线程中的异常,以及任何 应用程序类型(有或没有 用户界面)。然而,虽然 提供了一个很好的最后手段机制 对于记录未跟踪的异常,它 没有提供任何方法来防止 应用程序关闭和 没有办法压制。导航战鉴定小组 未处理的异常对话框。

    在生产应用中,显式 所有设备都需要进行异常处理 线程入口方法。一个人可以剪 使用包装器或助手工作 类来执行工作,例如 BackgroundWorker(在第部分中讨论 3.

        3
  •  3
  •   bohdan_trotsenko    16 年前

    保持简短的回答,是的,你可以防止运行时终止。

    以下是解决方法的演示:

    class Program
    {
        void Run()
        {
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
    
            Console.WriteLine("Press enter to exit.");
    
            do
            {
                (new Thread(delegate()
                {
                    throw new ArgumentException("ha-ha");
                })).Start();
    
            } while (Console.ReadLine().Trim().ToLowerInvariant() == "x");
    
    
            Console.WriteLine("last good-bye");
        }
    
        int r = 0;
    
        void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Interlocked.Increment(ref r);
            Console.WriteLine("handled. {0}", r);
            Console.WriteLine("Terminating " + e.IsTerminating.ToString());
    
            Thread.CurrentThread.IsBackground = true;
            Thread.CurrentThread.Name = "Dead thread";            
    
            while (true)
                Thread.Sleep(TimeSpan.FromHours(1));
            //Process.GetCurrentProcess().Kill();
        }
    
        static void Main(string[] args)
        {
            Console.WriteLine("...");
            (new Program()).Run();
        }
    }
    

    本质上,您只是没有让运行时显示“…程序已停止工作”对话框。

    如果您需要记录异常和 默默地 出口,你可以打电话 Process.GetCurrentProcess().Kill();

        4
  •  3
  •   Evgeny Gorbovoy    8 年前
        AppDomain.CurrentDomain.UnhandledException += (sender, e2) =>
        {
            Thread.CurrentThread.Join();
        };
    

    但请注意,此代码将冻结Thread和线程托管对象本身的所有堆栈内存。 但是,如果您的应用程序处于确定状态(可能是抛出LimitedDemoFunctionalityException或OperationStopWithUserMessageException),并且您没有开发24/7应用程序,则此技巧将奏效。

    最后,我认为MS应该允许开发人员从堆栈顶部覆盖未处理异常的逻辑。

        5
  •  1
  •   aku    17 年前

    以下是一篇关于这个问题的精彩博客文章: Handling "Unhandled Exceptions" in .NET 2.0

    在我看来,手动处理后台线程中的异常并在必要时通过回调重新抛出它们是正确的。

    delegate void ExceptionCallback(Exception ex);
    
    void MyExceptionCallback(Exception ex)
    {
       throw ex; // Handle/re-throw if necessary
    }
    
    void BackgroundThreadProc(Object obj)
    {
       try 
       { 
         throw new Exception(); 
       }
       catch (Exception ex)
       { 
         this.BeginInvoke(new ExceptionCallback(MyExceptionCallback), ex); 
       }
    }
    
    private void Test()
    {
       ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundThreadProc));
    }