永远不会触发TaskScheduler.UnobservedTaskException事件处理程序

我正在阅读一本关于C#任务并行库的书,并有以下示例,但永远不会触发TaskScheduler.UnobservedTaskException处理程序。 任何人都可以给我任何线索,为什么?

TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => { eventArgs.SetObserved(); ((AggregateException)eventArgs.Exception).Handle(ex => { Console.WriteLine("Exception type: {0}", ex.GetType()); return true; }); }; Task task1 = new Task(() => { throw new ArgumentNullException(); }); Task task2 = new Task(() => { throw new ArgumentOutOfRangeException(); }); task1.Start(); task2.Start(); while (!task1.IsCompleted || !task2.IsCompleted) { Thread.Sleep( 5000 ); } Console.WriteLine("done"); Console.ReadLine(); 

不幸的是,该示例永远不会向您展示您的代码。 UnobservedTaskException仅在GC收集任何未收到exception的情况下才会发生 – 只要您持有对task1task2的引用,GC将永远不会收集,并且您永远不会看到您的exception处理程序。

为了看到UnobservedTaskException的行为,我将尝试以下(人为的例子):

 public static void Main() { TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) => { eventArgs.SetObserved(); ((AggregateException)eventArgs.Exception).Handle(ex => { Console.WriteLine("Exception type: {0}", ex.GetType()); return true; }); }; Task.Factory.StartNew(() => { throw new ArgumentNullException(); }); Task.Factory.StartNew(() => { throw new ArgumentOutOfRangeException(); }); Thread.Sleep(100); GC.Collect(); GC.WaitForPendingFinalizers(); Console.WriteLine("Done"); Console.ReadKey(); } 

这将显示您的消息。 第一个Thread.Sleep(100)调用为任务提供了足够的时间。 收集和等待强制GC集合,它将激活您的事件处理程序2x。

该示例代码段中不会“未观察到”该exception。 直到垃圾收集器摆脱Task实例。 你必须像这样重写它:

 class Program { static void Main(string[] args) { TaskScheduler.UnobservedTaskException += ( object sender, UnobservedTaskExceptionEventArgs eventArgs ) => { eventArgs.SetObserved(); ( (AggregateException)eventArgs.Exception ).Handle( ex => { Console.WriteLine("Exception type: {0}", ex.GetType()); return true; } ); }; Run(); GC.Collect(); GC.WaitForPendingFinalizers(); Console.WriteLine("done"); Console.ReadLine(); } static void Run() { Task task1 = new Task(() => { throw new ArgumentNullException(); }); Task task2 = new Task(() => { throw new ArgumentOutOfRangeException(); }); task1.Start(); task2.Start(); while (!task1.IsCompleted || !task2.IsCompleted) { Thread.Sleep(50); } } } 

不要这样做,使用Task.Wait()。

我在.NET 4.5,Visual Studio 2012(调试或发布,无所谓)上运行此代码,我没有在我的app.config中放置ThrowUnobservedTaskException :

 using System; using System.Threading; using System.Threading.Tasks; namespace Test { public static class Program { private static void Main() { TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; RunTask(); Thread.Sleep(2000); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); Console.ReadLine(); } static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { Console.WriteLine("Caught!"); } private static void RunTask() { Task task = Task.Factory.StartNew(() => { Thread.Sleep(1000); // emulate some calculation Console.WriteLine("Before exception"); throw new Exception(); return 1; }); } } } 

并且exception由UnobservedTaskException处理程序捕获(打印出“Caught!”)。