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

时间:2008-10-09 11:36:55

标签: c# .net exception

我可以连接到AppDomain.CurrentDomain.UnhandledException来记录来自后台线程的异常,但是如何阻止它们终止运行时呢?

5 个答案:

答案 0 :(得分:38)

首先,你真的应该尽量不在背景线程中抛出异常 - 而不是处理 - 。如果您控制代理的运行方式,请将其封装在try catch块中,并想办法将异常信息传递回主线程(如果您显式调用BeginInvoke,则使用EndInvoke,或者通过在某处更新某些共享状态)。 / p>

忽略未处理的异常可能很危险。如果你有一个真正的不可处理的异常(想到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

答案 1 :(得分:9)

来自Joe Albahari的优秀threading文章:

  

.NET框架提供了一个   全局异常的低级事件   处理:   AppDomain.UnhandledException。这个   事件在未处理时触发   任何线程和任何线程中的异常   应用类型(有或没有   用户界面)。然而,虽然它   提供良好的最后手段机制   用于记录未捕获的异常,它   没有办法阻止   关闭申请 - 和   没有办法压制.NET   未处理的异常对话框。

     

在生产应用中,明确   所有人都需要处理异常   线程入口方法。人们可以削减   通过使用包装器或帮助器来工作   用于执行作业的类,例如   BackgroundWorker(在Part。中讨论)   3)。

答案 2 :(得分:3)

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

以下是解决方法的演示:

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();

答案 3 :(得分:1)

以下是关于此问题的精彩博文:Handling "Unhandled Exceptions" in .NET 2.0

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

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));
}

答案 4 :(得分:0)

    AppDomain.CurrentDomain.UnhandledException += (sender, e2) =>
    {
        Thread.CurrentThread.Join();
    };

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

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