What is the correct way to handle exceptions and UnobservedTaskException in the TPL?

Information from: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was

If you create a Task, and you don't ever call task.Wait() or try to retrieve the result of a Task<T>, when the task is collected by the garbage collector, it will tear down your application during finalization. For details, see MSDN's page on Exception Handling in the TPL.

The best option here is to "handle" the exception. This can be done via a continuation - you can attach a continuation to the task, and log/swallow/etc the exception that occurs. This provides a clean way to log task exceptions, and can be written as a simple extension method, ie:

public static void LogExceptions(this Task task)
{
    task.ContinueWith( t =>
    {
         var aggException = t.Exception.Flatten();
         foreach(var exception in aggException.InnerExceptions)
             LogException(exception);
    }, 
    TaskContinuationOptions.OnlyOnFaulted);
}

With the above, you can prevent any task from tearing down the app, and logging it, via:
Task.Factory.StartNew( () => 
   { 
       // Do your work...
   }).LogExceptions();

Alternatively, you can subscribe to the TaskScheduler.UnobservedTaskException and handle it there. 

Continuation of the task could determine the status of the parent task whether it get faulted or completed to success. A continuation can find out if an exception was thrown by the antecedent Task by the antecedent task's exception property. The following code snippet will print the results of a NullReferenceException to the console.

Task task1 = Task.Factory.StartNew (() => { throw null; });
Task task2 = task1.ContinueWith (ant => Console.Write(ant.Exception());

If task1 throws an exception and this exception is not captured/queried by the continuation then it is considered unhandled and the application get halted. With continuations it is enough to establish the result of the task via the Status property.
asyncTask.ContinueWith(task =>
{
    // Check task status.
    switch (task.Status)
    {
        // Handle any exceptions to prevent UnobservedTaskException.             
        case TaskStatus.RanToCompletion:
            if (asyncTask.Result)
            {
                // Do stuff...
            }
            break;
        case TaskStatus.Faulted:
            if (task.Exception != null)
                mainForm.progressRightLabelText = task.Exception.InnerException.Message;
            else
                mainForm.progressRightLabelText = "Operation failed!";
        default:
            break;
    }
}


If you don't use continuations you either have to wait on the task in a try/catch block or query a task's Result in a try/catch block. See below code snippet:
int x = 0;
Task task = Task.Factory.StartNew (() => 7 / x);
try
{
    task.Wait();
    // OR.
    int result = task.Result;
}
catch (AggregateException aggEx)
{
    Console.WriteLine(aggEx.InnerException.Message);
}

Debugging Threads with Concurrency Visualizer

Features of Concurrency Visualizer:

  • Shift+Alt+F5
  • Fast processing, faster loading
           o Supports big traces
  • Supports big traces
  • Support s EventSource and custom markers
            o Built-in support for TPL, PLINQ, Sync Data Structures, and Dataflow
            o Built-in support for your own EventSource types
                    § Also provides Visual Studio markers API
  • New Visualizations
             o e.g. defender view

Demo:

Here are few steps to know that how to utilize “Concurrency Visualizer” for multithreaded applications.

  1. Create a Console project.
  2. Create custom EventSource to trace in the visualizer as below:
    [EventSource(Guid = "7BBB4E52-7DA7-45EB-B6C2-C01D460A087C")]
    class MyEventSource : EventSource
    {
        internal static MyEventSource log = new MyEventSource();
    
        [Event(1)]
        public void SomeEventHandled(int data)
        {
            WriteEvent(1, data);
        }
    }

    To provide GUID to the event source use “guidgen” and copy newly generated GUID. Then put it in the attribute of the EventSource class.



  3. Now complete the test code to proceed the demo.

    Full code snippet:
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace ConcurrencyVisualizerDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                var list = new List<Task>();
                for (int i = 0; i < 10; i++)
                {
                    list.Add(Task.Run(() =>
                        {
                            //Loging here
                            MyEventSource.log.SomeEventHandled(21);
                            Thread.SpinWait(1000000);
                        }));
                }
                Task.WaitAll(list.ToArray());
            }
    
            [EventSource(Guid = "7BBB4E52-7DA7-45EB-B6C2-C01D460A087C")]
            class MyEventSource : EventSource
            {
                internal static MyEventSource log = new MyEventSource();
    
                [Event(1)]
                public void SomeEventHandled(int data)
                {
                    WriteEvent(1, data);
                }
            }
        }
    }
  4. There is optional extension “Concurrency Visualizer” for visual studio, which visual all the data to debug the concurrency between the treads. After installing this, you can find the option under Analyze menu. See image below:

    clip_image004
  5. Now copy the GUID  of MyEventSource and go to the Concurrency Visualizer’s settings. Then add new provider and put this copied guid in the provider.
    Analyze->Concurrency Visualizer->Advance Settings->Markers

    image

    Provide name and paste copied guid in the Privider GUID field. Click ok to complete provider addition.
  6. Now start Concurrency Visualizer by pressing Shift+Alt+F5 and it will start Shift+Alt+F5. Now it will start generating reports. See the below images:

    image

    image

    image

    image