2008-10-09 152 views

回答

35

首先,你真的应该尽量避免在后台线程中抛出异常并且不处理异常。如果你控制你的委托经营的方式,将其封装在一个try catch块和(使用EndInvoke会,如果你显式调用的BeginInvoke,或更新某个地方某个共享状态)计算的方式来传递异常信息返回到你的主线程。

忽略一个未处理的异常可能是危险的。如果你有一个真正的不可处理的异常(OutOfMemoryException记住),那么你无法做任何事情,你的过程基本上是注定要失败的。

返回到.NET 1.1,在backgroundthread未处理的异常也只是抛出无处和主线程会乐意犁上。这可能会带来讨厌的影响。所以在.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

+0

有尝试...赶上各地的臭味。我只想在未处理的位置只需要一个,并且不希望我的应用程序因为用户去点击“功能X”时发生的简单的空指针而死亡,并且我们告诉他们使用正在工作的其他功能X按钮。 。它并没有关闭应用程序,每次都会让用户感到懊恼,就像原来的bug一样恼火(引导应用程序需要时间,因此会给用户带来麻烦) – 2011-12-30 18:31:54

+0

我遇到了有问题的专有外部库它会使用(内部)`ArgumentNullException`异常地崩溃应用程序。我想为那个库启用`legacyUnhandledExceptionPolicy`。这可以做到吗? – chris 2015-05-14 11:15:46

7

从乔阿尔巴哈利的优秀threading文章:

.NET Framework提供了全局异常 处理一个 较低级别的事件: AppDomain.UnhandledException。此 事件触发时,有未处理 例外在任何线程中,并且在任何 类型的应用程序(具有或不具有 用户界面)。然而,尽管 提供了一个良好的最后手段机制 用于记录未捕获的例外,它 提供防止关闭 应用程序没有办法 - 和 绝非打压.NET 未处理的异常对话框。

在生产应用程序中,所有 线程输入方法都需要明确的 异常处理。人们可以通过使用包装或辅助 类来执行该作业切割 工作,如 BackgroundWorker的(在 第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

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

下面是解决方法的演示:

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

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

但是要小心,这个代码将冻结线程和线程的管理对象自身的所有堆栈存储器。 但是,如果您的应用程序处于确定状态(可能是您抛出了LimitedDemoFunctionalityException或OperationStopWithUserMessageException),并且您没有开发全天候应用程序,则此技巧将起作用。

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