Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TaskScheduler.UnobservedTaskException event handler never being triggered

I'm reading through a book about the C# Task Parallel Library and have the following example but the TaskScheduler.UnobservedTaskException handler is never being triggered. Can anyone give me any clues as to why?

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(); 
like image 467
devlife Avatar asked Jul 19 '10 19:07

devlife


2 Answers

Unfortunately, that example will never show you your code. The UnobservedTaskException will only happen if a Task gets collected by the GC with an exception unobserved - as long as you hold a reference to task1 and task2, the GC will never collect, and you'll never see your exception handler.

In order to see the behavior of the UnobservedTaskException in action, I'd try the following (contrived example):

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

This will show you your messages. The first Thread.Sleep(100) call provides enough time for the tasks to throw. The collect and wait forces a GC collection, which will fire your event handler 2x.

like image 165
Reed Copsey Avatar answered Sep 20 '22 06:09

Reed Copsey


The exception won't be "unobserved" in that sample snippet. Not until the garbage collector gets rid of the Task instances. You'd have to rewrite it like this:

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

Don't do this, use Task.Wait().

like image 39
Hans Passant Avatar answered Sep 21 '22 06:09

Hans Passant