为什么后台线程中未处理的异常不会导致应用程序域崩溃?

时间:2011-06-24 08:52:36

标签: c# .net multithreading exception-handling delegates

我完全不解。如果在我从未测试过的线程中存在未捕获的异常,我确信.NET会关闭整个应用程序域。

但是我只是尝试了以下代码并且它没有失败......有谁能解释原因?

(在.NET 4和3.5中试过)

static void Main(string[] args)
{
    Console.WriteLine("Main thread {0}", Thread.CurrentThread.ManagedThreadId);

    Action a = new Action(() =>
    {
        Console.WriteLine("Background thread {0}", Thread.CurrentThread.ManagedThreadId);

        throw new ApplicationException("test exception");
    });

    a.BeginInvoke(null, null);

    Console.ReadLine();
}

4 个答案:

答案 0 :(得分:9)

这种情况正在发生,因为BeginInvoke在内部使用ThreadPool,而当ThreadPool任何未经编码的例外情况都会导致沉默失败。但是,如果您使用a.EndInvoke,那么未加工的异常将以EndInvoke方法抛出。

注意:正如João Angelo所述,直接使用ThreadPool方法“如ThreadPool.QueueUserWorkItemsUnsafeQueueUserWorkItem”会在2.0及更高版本处抛出异常。

答案 1 :(得分:6)

来自MSDN上的Exceptions in Managed Threads

  

在.NET Framework 2.0版中,   公共语言运行时允许最多   线程中未处理的异常   自然地进行。在大多数情况下这个   表示未处理的异常   导致应用程序终止。

     

这是一个重大变化   .NET Framework版本1.0和1.1,   这为许多人提供了支持   未处理的例外 - 例如,   线程池中未处理的异常   线程。请参阅从上一个更改   本主题后面的版本。

     

作为临时兼容措施,   管理员可以放置一个   兼容性标志   申请部分   配置文件。这导致了   要恢复的公共语言运行库   版本1.0和1.1的行为。

<legacyUnhandledExceptionPolicy enabled="1"/>

答案 2 :(得分:3)

通常使用异步委托,如果委托方法抛出异常,则线程终止,并且当您调用EndInvoke时,将仅在调用代码 中再次抛出异常。

这就是为什么在使用异步委托(BeginInvoke)时,您应该始终调用EndInvoke。此外,这不应与Control.BeginInvoke相混淆,OneWay可以用火与忘记的方式调用。

之前我通常说过,因为如果委托方法返回void,你有可能声明应该忽略异常。为此,您需要使用willNotIgnoreThrow.EndInvoke属性标记方法。

如果您运行以下示例,则只会在调用static void Throws() { Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId); throw new ApplicationException("Test 1"); } [OneWay] static void ThrowsButIsIgnored() { Console.WriteLine("Thread: {0}", Thread.CurrentThread.ManagedThreadId); throw new ApplicationException("Test 2"); } static void Main(string[] args) { Console.WriteLine("Main: {0}", Thread.CurrentThread.ManagedThreadId); var willIgnoreThrow = new Action(ThrowsButIsIgnored); var result1 = willIgnoreThrow.BeginInvoke(null, null); Console.ReadLine(); willIgnoreThrow.EndInvoke(result1); Console.WriteLine("============================"); var willNotIgnoreThrow = new Action(Throws); var result2 = willNotIgnoreThrow.BeginInvoke(null, null); Console.ReadLine(); willNotIgnoreThrow.EndInvoke(result2); } 时收到异常。

{{1}}

答案 3 :(得分:0)

因为对给定线程的异常抛出会保留在那里,除非它被引导回主线程。

这就是backgroundWorker为你做的,如果你在BackgroundWorker的线程中有异常,它会在主线程上重新抛出。

a.BeginInvoke(null, null);

这会产生异步调用,这会创建另一个执行此

的线程