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

我完全不解。 如果在我从未测试过的线程中有未捕获的exception,我非常确定.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(); } 

发生这种情况是因为BeginInvoke内部使用ThreadPool而当ThreadPool任何未处理的exception将是静默失败。 但是,如果使用a.EndInvoke ,则将在EndInvoke方法中抛出未处理的exception。

注意:正如UnsafeQueueUserWorkItem所说,直接使用ThreadPool方法“喜欢ThreadPool.QueueUserWorkItemsUnsafeQueueUserWorkItem ”会抛出2.0及以上的exception。

从MSDN上的托管线程中的例外 :

在.NET Framework 2.0版中,公共语言运行库允许线程中大多数未处理的exception自然地继续。 在大多数情况下,这意味着未处理的exception会导致应用程序终止。

这是.NET Framework版本1.0和1.1的重大变化,它为许多未处理的exception提供了一个支持 – 例如,线程池线程中的未处理exception。 请参阅本主题后面的“从先前版本更改”。

作为临时兼容性措施,管理员可以在应用程序配置文件的部分中放置兼容性标志。 这会导致公共语言运行库恢复到版本1.0和1.1的行为。

  

通常使用异步委托,如果委托方法抛出exception,则线程终止,并且只有在调用EndInvoke才会在调用代码中再次抛出exception。

这就是使用异步委托( BeginInvoke )时应始终调用EndInvoke 。 此外,这不应与Control.BeginInvoke混淆,后者可以以一种火灾和忘记的方式调用。

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

如果运行以下示例,则在调用willNotIgnoreThrow.EndInvoke时只会出现exception。

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

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

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

 a.BeginInvoke(null, null); 

这会产生异步调用,这会创建另一个执行此操作的线程