BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Parallel and Asynchronous
.NET 4.0+

Nested Tasks

The twelfth part of the Parallel Programming in .NET tutorial looks at the ability for tasks to be nested. A nested task is one that is created and executed within the delegate of another. Nested tasks are not linked to their parent tasks.

Catching Nested Task Exceptions

In order to catch exceptions from nested tasks you must add wait for them to complete within the parent task. This will prevent the parent task from exiting before the nested tasks have finished executing. If the Wait methods are called within a try / catch block you can obtain the exceptions and either aggregate and propagate them, or handle them and recover gracefully. If you omit the try / catch block the exceptions will cause the parent task to fail and the exceptions will be included in the AggregateException that it throws.

The following simplified example creates an outer task and two nested tasks. Each nested task throws an exception and each is waited upon. This causes the two exceptions to be propagated through the parent task and, finally, caught in the try / catch block containing the outer task's Wait call. Note that the caught AggregateException contains only one inner exception, which itself is an aggregation of the two child tasks' exceptions. This is because a first AggregateException is generated by the call to Task.WaitAll within the parent task. This is wrapped in a second AggregateException by the call to outerTask.Wait.

var outerTask = new Task(() =>
{
    var innerTask1 = new Task(() =>
    {
        throw new Exception("Inner Task 1 Failed");
    });
    innerTask1.Start();

    var innerTask2 = new Task(() =>
    {
        throw new Exception("Inner Task 2 Failed");
    });
    innerTask2.Start();

    Task.WaitAll(innerTask1, innerTask2);
});

outerTask.Start();

try
{
    outerTask.Wait();
}
catch (AggregateException ex)
{
    foreach (var exception in ex.InnerExceptions)
    {
        Console.WriteLine(exception.Message);
    }
}

/*
            
One or more errors occurred.
             
*/

Flattening AggregateExceptions

As your hierarchy of nested tasks grows in complexity, so the hierarchy of exceptions that it may generate grows. You may find that you are catching AggregateExceptions containing many levels of nested exception. You could write a method that flattens the hierarchy of inner exceptions in order to obtain a simple list. Luckily, you do not need to, as one already exists within the .NET framework. If you call the Flatten method of an AggregateException, a new AggregateException is returned. This contains all of the inner exceptions from all levels of the hierarchy. The InnerExceptions property of the new exception is then easy to enumerate.

The final example is a slightly modified version of the previous one. This time the caught exception is flattened so that all of the inner exceptions can be easily displayed.

var outerTask = new Task(() =>
{
    var innerTask1 = new Task(() =>
    {
        throw new Exception("Inner Task 1 Failed");
    });
    innerTask1.Start();

    var innerTask2 = new Task(() =>
    {
        throw new Exception("Inner Task 2 Failed");
    });
    innerTask2.Start();

    Task.WaitAll(innerTask1, innerTask2);
});

outerTask.Start();

try
{
    outerTask.Wait();
}
catch (AggregateException ex)
{
    foreach (var exception in ex.Flatten().InnerExceptions)
    {
        Console.WriteLine(exception.Message);
    }
}

/*
            
Inner Task 1 Failed
Inner Task 2 Failed
             
*/
4 November 2011