Recall that if exceptions thrown from Task bodies are left unobserved, they will be escalated. In .NET 4, this means that TPL will throw them on the finalizer after the Task objects are available for garbage collection. The UnobservedTaskException event on the TaskScheduler class was added as a last-resort method to observe such exceptions and prevent them from crashing the process. Therefore, code like the following will not trigger the event:
TaskScheduler.UnobservedTaskException +=
(object sender, UnobservedTaskExceptionEventArgs excArgs) =>
{
Console.WriteLine(“Caught it!”);
excArgs.SetObserved();
};
Task t = Task.Factory.StartNew(() =>
{
throw new Exception(“ha!”);
});
try { t.Wait(); }
catch (Exception) { }
This is because the Task exception is already being correctly observed by calling Wait()! There’s no reason to trigger the event, because the exception will not be escalated anyway.
Note that even if the Wait() call is removed from the code, the event will not trigger until the Task is available for garbage collection (and a collection actually happens). Thus, one way to see the event in action is to do something like this (replacing the try/catch block above):
((IAsyncResult)t).AsyncWaitHandle.WaitOne();
t = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
First, we wait for the Task to complete using its underlying wait handle; this does not observe exceptions like calling Task.Wait() does. Then, we null the Task, making it available for garbage collection, and force a full collection. See the attached file for the code in entirety.
NOTE: This sample is intended to let folks experiment with the UnobservedTaskException event. It is certainly not recommended for production code.
0 comments