为什么后台线程中未处理的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.QueueUserWorkItems
和UnsafeQueueUserWorkItem
”会抛出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);
这会产生异步调用,这会创建另一个执行此操作的线程