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+

Continuation Tasks

The eleventh part of the Parallel Programming in .NET tutorial considers the use of continuation tasks. These are parallel tasks that start automatically when one or more other tasks complete, allowing a chain of dependent tasks to be executed correctly.

Handling Exceptions with Continuations

All of the above examples have been simplified by excluding exception handling. This, hopefully, makes it easier to see the structure of the code that relates to the antecedents and continuations. Of course, exception handling is very important so the final example will add this.

It is important to understand that there is no relationship between antecedents and continuations, except for that which controls scheduling. Specifically, exceptions thrown by a task are not propagated to its linked continuation tasks. This means that you should check for exceptions in all of the tasks that you run. The simplest solution is to wait for all of the tasks that may error to complete and surround the Wait method with a Try / Catch block. As with other parallel constructs, exceptions are wrapped in an AggregateException.

In the final example we see one antecedent and one continuation. Once the antecedent is started, the Task.WaitAll method is used to wait for both tasks to complete. This is wrapped with the try portion of a try / catch block. Note that both tasks throw an exception and both exceptions are present in the caught AggregateException. This highlights that even when an antecedent fails, its continuations still run.

string userID = null;

var loadUserDataTask = new Task(() =>
{
    Console.WriteLine("Loading User Data");
    Thread.Sleep(2000);
    userID = "1234";
    Console.WriteLine("User data loaded");
    throw new Exception("Load User Data Exception");
});

var loadUserPermissionsTask = loadUserDataTask.ContinueWith(t =>
{
    Console.WriteLine("Loading User Permissions for user {0}", userID);
    Thread.Sleep(2000);
    Console.WriteLine("User permissions loaded");
    throw new Exception("Load User Permissions Exception");
});

loadUserDataTask.Start();

try
{
    Task.WaitAll(loadUserDataTask, loadUserPermissionsTask);
}
catch (AggregateException ex)
{
    foreach (var exception in ex.InnerExceptions)
    {
        Console.WriteLine(exception.Message);
    }
}

Console.WriteLine("CRM Application Loaded");

loadUserDataTask.Dispose();
loadUserPermissionsTask.Dispose();

/* OUTPUT

Loading User Data
User data loaded
Loading User Permissions for user 1234
User permissions loaded
Load User Permissions Exception
Load User Data Exception
CRM Application Loaded

*/
24 October 2011