Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#

Task Parallel Library and async-await Functionality - Patterns of Usage in Easy Samples

Rate me:
Please Sign up or sign in to vote.
4.98/5 (94 votes)
13 Apr 2013CPOL23 min read 342.2K   3.5K   303   103
TPL/Async tutorial with emphasis on how to use it and why it is needed

Introduction

Why I am Writing this Article

A lot of tutorials have already been written about TPL and the new .NET 4.5 async-await features, the most prominent examples are Task Parallel Library: 1 of n, Task Parallel Library: 2 of n, Task Parallel Library: 6 of n and Threading in C#, PART 5: PARALLEL PROGRAMMING.

Here I present my own version of a TPL and async-await tutorial demonstrating why TPL and async-await functionality is so useful and how to apply it to some well-known problems.

The Purpose of TPL and async-await Features 

TPL and async-await features simplify dealing with asynchronous calls. There maybe several reasons to have asynchronous calls in your code.

Most software products, do not act in a vacuum - they has to contact other systems. The common examples of the need for such communications are UI or web client contacting the server to read or update some data or the middleware contacting the databases, etc.

When such communications occur, the software on the client side cannot control or predict the speed of the response from the server. If the client sends a synchronous request to the server, the calling thread blocks until the server response is obtained and processed. Such blockage can lead to e.g. GUI or web application freezing completely for an unspecified period of time - which is definitely not desirable. On the positive side, however, the synchronous calls, preserve the program structure - calling the server synchronously is as easy (from the developer's point of view) as making a local call.

We can also solve the communication problem by using asynchronous communications with the server. Most common way to do it, is to register a callback on the client to fire when the server returns information, then to send an asynchronous request to the server - a request that does not block the current thread. After the server responds, the callback code will update the client based on the information obtained from the server. The problem with this approach is that the flow of the code is broken into two different pieces - the code before the call to the server and the callback code that runs after the server responds. In other words - async calls to the server dramatically change the structure of the code. When you need to make only one call to the server at a time, you can still handle the situation somehow at the expense of making the code a little worse - having two functions per call as described above, but what if you need to have multiple calls to the server - some running in parallel, some sequential, some have to be executed only after a number of previous asynchronous calls have completed. Asynchronous programming in that case can easily lead to a developer's nightmare - more and more callbacks will have to be inserted for every async call breaking the program into a bunch of pieces that have to be connected logically. Also some of the callbacks will have to be synchronized in order to detect their completion before starting some other processing.

There can be reasons other than calling the server for making asynchronous calls, e.g. you might want to do some processing on different threads in order to better utilize the multiple cores on your machine or because such processing has been built into the framework that you are using.

The purpose of TPL and the async-await features is to make the asynchronous programming almost as simple as the synchronous programming, allowing the developers to preserve the logical structure of the code even when it executes asynchronously.

Organization of the Article 

First I provide a refresher on TPL and async-await features. Then I show how to apply them to problems you are likely encounter - in particular to making calls to web services, scheduling mutltiple background workers and animation storyboards.

Software Tools Used

We use Visual Studio 2012 and .NET 4.5 for creating samples for this article. The TPL features (without async-await and some other .NET 4.5 features) can be all run under .NET 4.0 within Visual Studio 2010.

Refresher on the TPL

For the full coverage of the TPL features, please, see the links we provided at the beginning of the article - here we only describe the features that we consider most useful.

Tasks

TPL's basic unit of code is Task. Tasks can run, can be waited for to finish. Tasks can have child tasks. The code can wait on multiple tasks to finish before it proceeds. Tasks can be cancelled and their progress can be monitored.

Task can only run from start to finish once - you cannot run the same task object two times. If you need to re-run the Task, you'll need to create another Task object for running the same code.

By default the tasks run on a thread from a thread pool. TaskScheduler allows the developer to customize the thread for running the task. Some tasks, that use TaskCompletionSource do not spawn a separate thread.

Simple Task Sample

The simplest sample showing the basic task features is located under TaskSample solution. Here is its code:
// task that does not return a value
Task t1 = new Task
(

    () =>
    {
        Thread.Sleep(1000);
        Console.WriteLine("Task Completed");
    }
);

// start the task
t1.Start();

// wait for the task to finish
t1.Wait();

Console.WriteLine("End of Main");

// make sure the application console window does not 
// disappear when run under the VS debugger.
Console.ReadLine();

Pretty self-explanatory - we define the task to sleep for 1 second and then to print "Task Completed" on the console. We start the task after we define it, print "We are waiting for the task to finish" and then call Wait method to block the main program until the task finishes.

Simple Task Throwing Exception

If the task throws an exception, such exception can be caught in the main thread by surrounding Wait() method with try-catch clause, as shown under TaskWithException project:

// task that does not return a value
Task t1 = new Task
(

    () =>
    {
        Thread.Sleep(1000);

        // throw an exception within a task
        throw new Exception("This is an Exception within a task");

        Console.WriteLine("Task Completed");
    }
);

// start the task
t1.Start();

Console.WriteLine("We are waiting for the task to finish");

// wait for the task to finish
try
{
    t1.Wait();
}
catch (AggregateException e)
{
    // task framework wraps the thrown exception within
    // an aggregate exception
    Console.WriteLine("Caught Exception " + e.InnerException.Message);
}

Console.WriteLine("End of Main");

// make sure the application console window does not 
// disappear when run under the VS debugger.
Console.ReadLine();

Waiting with Timeout

Function Wait() has a version that takes timeout parameter. Such version returns bool value set to false when the timeout occurred and true otherwise. The corresponding sample is located under TaskWithTimeout project:

// task that does not return a value
Task t1 = new Task
(

    () =>
    {
        // sleep for 4 seconds
        Thread.Sleep(4000);
        Console.WriteLine("Task Completed");
    }
);

// start the task
t1.Start();

Console.WriteLine("We are waiting for the task to finish");

// wait for 2 seconds for the task to finish
bool hasNotTimedOut = t1.Wait(2000);

if (hasNotTimedOut)
{
    Console.WriteLine("The task has not timed out");
}
else
{
    Console.WriteLine("The task has timed out");
}
Console.WriteLine("End of Main");

// make sure the application console window does not 
// disappear when run under the VS debugger.
Console.ReadLine();

The main program waits for two seconds for the task to finish and then continues. Note that the task is not cancelled in this case - it continues after the sleep period and "Task Completed" message is printed at the very end - after the "End of Main" message.

Cancelling a Task

Project TaskCancellation shows how to cancel a task:

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

CancellationToken cancellationToken = cancellationTokenSource.Token;
// task that does not return a value
Task t1 = new Task
(

    () =>
    {
        int i = 0;
        while (true) // create a task that iterates forever unless cancelled
        {
            i++;
            Thread.Sleep(1000);

            Console.WriteLine("This is task iteration " + i);

            // if cancellation requested - throw OperationCancelledException
            cancellationToken.ThrowIfCancellationRequested();
        }
    },
    cancellationToken // pass the cancellation token as an argument to the task constructor
);

// start the task
t1.Start();

// sleep for 3 seconds
Thread.Sleep(3000);

// request for the task to cancel
cancellationTokenSource.Cancel();

try
{
    t1.Wait();
}
catch (AggregateException e)
{
    if (e.InnerException is OperationCanceledException)
    {
        Console.WriteLine("The operation has been cancelled");
    }
    else
    {
        Console.WriteLine("Some unexpected exception");
    }
}

Console.WriteLine("End of Main");

// make sure the application console window does not 
// disappear when run under the VS debugger.
Console.ReadLine();  

First, we create CancellationTokenSource. Its property Token contains a CancellationToken object. This object is passed to the Task's constructor.

After starting the Task, we wait 3 seconds and call Cancel() method on the CancellationTokenSource. Within the task, we call cancellationToken.ThrowIfCancellationRequested() method at the end of each iteration. This method throws an OperationCanceledException if cancellation was requested, otherwise, it does nothing. When OperationCanceledException propagates outside of the Task's body, it is wrapped within AggregateException - and this is the exception that we catch within Main.

Tasks with Child Tasks

You can start a Task within another Task's body. In that case, Task started inside another Task's body is called the child Task and the Task in whose body the child Task starts is called the parent Task. A Task constructor has an override that accepts TaskCreationOptions argument. When this argument is specified to include TaskCreationOptions.AttachedToParent option, the created child Task becomes "attached" to its parent. This "attachment" creates very useful dependencies between the parent and the child tasks in that:

  • The parent Task won't complete until all of its attached child Tasks have completed.
  • The parent-child Tasks create a hierarchy very similar to function stack - only here we have child Tasks possibly executing in different threads. We call it "attached child stack". If an attached child throws an exception, the exception will be propagated up the "attached child stack" - wrapped by another AggregateException at each step of its propagation. It can be caught at any level within the "attached child stack"

A Task created within a parent Task's body without AttachedToParent flag is called "detached". There are no dependencies between a parent Task and its "detached" child Tasks and we are not very interested in "detached" child Tasks.

TaskWithChildrent project presents the attached child Task functionality:

// task that does not return a value
Task task = new Task
(
    () =>
    {
        Task childTask = new Task
        (
            () =>
            {
                Thread.Sleep(1000);

                Task grandChildTask = new Task
                (
                    () =>
                    {
                        Thread.Sleep(2000);

                        // throw an exception from within grand-child's body
                        throw new Exception("Grandchild exception");
                    },
                    TaskCreationOptions.AttachedToParent
                );

                grandChildTask.Start();
            },
            TaskCreationOptions.AttachedToParent
        );

        childTask.Start();
    }
);

// start the task
task.Start();

try
{
    // wait function will throw the exceptions from
    // the child tasks
    task.Wait();
    Console.WriteLine("The task is finished");
}
catch (AggregateException e)
{
    // we catch the exception thrown by the grand-child and display its message
    Console.WriteLine("Exception caught: " + e.InnerException.InnerException.InnerException.Message);
}
Console.WriteLine("End of Main");

// make sure the application console window does not 
// disappear when run under the VS debugger.
Console.ReadLine();

An exception is thrown at the grand-child level and caught at the top level within Main function. As was mentioned about, it is wrapped by a new AggregateException as it passes each level within the "attached child stack", so, in order to get to the original exception, we need to use InnerException property several times: e.InnerException.InnerException.InnerException.Message.

Tasks that Return Result

If you want a Task to product some result retrieavable from the Task itself, you can use Task<TResult> generic form of it. The generic argument TResult specifies the type of the Task's result. Everything discussed above still applies to such tasks. The only extra functionality available from such Tasks is the property

TResult Task<TResult>.Result;

It blocks while the Task is running (just like Wait() method does) and it returns the result after the Task's run is completed.

Task.WhenAll() and Task.WhenAny()

In .NET 4.5, there is an important static method on Task class - Task.WhenAll(). It returns a Task object that completes when and only when all the Task objects passed to it are completed. There is a number of method overrides - some versions of the method accept a parameter list - others an array of Task objects. If the input Tasks return results, the Task returned by Task.WhenAll() method can return an array of values corresponding to the results from the individual input Task objects.

Another method Task.WhenAny() creates a Task that waits only for one Task to complete out of many. It is used less often, but can be quite useful in several scenarios.

There are also methods that can block the current thread's execution until a number of Tasks is finished of one Task out of the number is finished without creating new Tasks. These methods are Task.WaitAll(...) and Task.WaitAny(...)

ContinueWith Method

There is a Task method ContinueWith that allows to specify functionality you want to be executed after the task is finished - a kind of a callback to the Task.

Code for testing this method is located under ContinueWithTest project.

// create Task t1
Task t1 = new Task
(
    () =>
    {
        Thread.Sleep(1000);

        // make the Task throw an exception
        throw new Exception("this is an exception");

        Console.WriteLine("This is T1");
    }
);

Task t2 = t1.ContinueWith
(
    (predecessorTask) =>
    {
        // exception should be checked for null within the 
        // continuation functionality. An exception that is not
        // checked might bring the application down
        if (predecessorTask.Exception != null)
        {
            Console.WriteLine("Predecessor Exception within Continuation");
            return;
        }

            Thread.Sleep(1000);

            Console.WriteLine("This is Continuation");
    },
    // attach the continuation to parent and the continuation 
    // functionality work only if the predecessor task ran to completion
    // without exceptions thrown or without cancellations.
    TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.OnlyOnRanToCompletion
);

t1.Start();

try
{
    t1.Wait();
    t2.Wait();
}
catch (AggregateException e)
{
    Console.WriteLine(e.InnerException.Message);
}  

We create Task t2 to be the continuation of the Task t1. By passing TaskContinuationOptions.OnlyOnRanToCompletion option we make the continuation only run in case the predecessor Task ran successfully. Since in our case, the predecessor Task threw an exception, the successor Task will never be called.

ContinueWhenAll and ContinueWhenAny

Two methods can be used to create continuation Tasks to start after a number of predecessor Tasks are completed: Task.Factory.ContinueWhenAll(...) and Task.Factory.ContinueWhenAny(...). In fact these two methods are very similar to the methods WhenAll and WhenAny described above, only they were part of .NET 4.0 while WhenAll and WhenAny were added in .NET 4.5.

Task Progress

.NET 4.5 added IProgress<T> interface and Progress<T> class to facilitate measuring the progress of a Task or any other code. Both accept a generic type parameter T to specify the type of the value that measures progress. Progress<T> implicitely implements IProgress<T> interface.

IProgress<T> has method Report(T ) that should be called to report progress. Progress<T> has an event ProgressChanged that fires every time the progress is reported. Second argument to this event is the progress value. An event handler can be attached to this event to handle the progress reporting.

TaskProgressTest solution gives a usage example for progress functionality:

Progress<string> progress = new Progress<string>();

Task t = new Task
(
    () =>
    {
        for (int i = 0; i < 5; i++)
        {
            Thread.Sleep(1000);

            // use IProgress'es Report() function to report progress
            ((IProgress<string>)progress).Report("" + i);
        }
    }
);

progress.ProgressChanged += (sender, str) =>
    {
        // print the current progress
        // when it is reported
        Console.WriteLine("Progress is " + str);
    };

t.Start();

t.Wait();  

The Task t iterates 5 times and uses the iteration index number i as the progress indicator (this number is passed to IProgress<string>.Report(...) function).

ProgressChanged event handler prints the reported progress to the console.

TaskCompletionSource and Wrapping Event Based Functionality within a Task

A lot of asynchronous functionality coming as part of .NET or from some 3-rd parties is built based on the Event-based Asynchronous Pattern (or EAP). The principle of the EAP is that the async functionality provides a method to start the asynchronous operation and a callback event that allows attaching a callback event handler that will be called once the asynchronous operation completes. The client code first attaches the callback handler to the async functionality and then calls the method to start the async operation.

Examples of such EAP functionality are BackgroundWorker, asynchronous service references in .NET 4.0 etc.

Much of .NET 4.5 functionality has been changed to Tasks and BackgroundWorker is all but obsolete now, but you still might come across some instances when it is necessary to use the EAP pattern. In such cases, it will be nice to produce Task objects out of the EAP functionality so that you'll be able to schedule them to run in parallel or wait for a number of previous Tasks to finish etc. In this section we show how to achieve it.

We use TaskCompletionSource class to produce Tasks that do not create their own threading and instead use other object's asynchronous functionality.

The EAP functionality is located under MyEAPTest class. It provides function StartAsync() that runs a method in a separate thread printing a string that is passed to it. At the end of the async method, EAPTestCompleted event is fired:

public class MyEAPTest
{
    // thread to run the async action on
    Thread _thread;

    // event to fire on completing the async action
    public event Action EAPTestCompleted;

    public MyEAPTest(string stringToPrint)
    {
        _thread = new Thread
        (
            (ThreadStart) (() =>
            {
                Thread.Sleep(1000); // 1 second delay 
                Console.WriteLine(stringToPrint); // print the passed string
                if (EAPTestCompleted != null)
                    EAPTestCompleted();
            })
        );
    }

    // function to start the async action
    public void StartAsync()
    {
        _thread.Start();
    }
}  

EPAToTaskUtils class provides ToTask() static extension method converting the MyEAPTest object into a Task object:

public static Task<object> ToTask(this MyEAPTest epaTest)
{
    // TaskCompletionSource should always have a generic
    // type argument so since we are not using any result,
    // we just set the argument to be of type object and will 
    // pass null to indicate the task's completion
    TaskCompletionSource<object> taskCompletionSource =
        new TaskCompletionSource<object>(TaskCreationOptions.AttachedToParent);

    Action onEpaTestCompleted = null;

    onEpaTestCompleted = () =>
        {
            epaTest.EAPTestCompleted -= onEpaTestCompleted;
            
            // we are setting the result to null to indicate 
            // the completion of the async opration
            taskCompletionSource.SetResult(null);
        };

    epaTest.EAPTestCompleted += onEpaTestCompleted;

    // start the async operation when the task is created
    epaTest.StartAsync();

    return taskCompletionSource.Task;
}

Note that ToTask() method described above not only creates a Task but also starts it. In fact, it is not recommended that you create a Task out of EAP without starting it, since the TaskCompletionSource's Task returned will not have a valid Start method implementation, which will be confusing for the users of the functionality. The fact that the Task starts within the conversion ToTask method, does not limit the use of the functionality, however, since we can always postpone calling the ToTask until we are ready to start the Task.

Calling SetResult method on TaskCompletionSource will cause its Task to stop blocking. You can also use TaskCompletionSource.SetCanceled() method to throw OperationCancelledException from the blocking methods or, you can use TaskCompletionSource.SetException(...) method that will make the blocking method of the TaskCompletionSource's Task to behave as if an exception was thrown during the Task execution. In particular, if the TaskCompletionSource was created with AttachedToParent creation option, the exception will propagate up the "attached child stack".

Here is the code that creates and schedules the Tasks:

// create the epa tests
MyEAPTest epaTest1 = new MyEAPTest("this is EPA test1");
MyEAPTest epaTest2 = new MyEAPTest("this is EPA test2");
MyEAPTest epaTest3 = new MyEAPTest("this is EPA test3");

// create the tasks - note that 
// the task starts the moment it is created. 
Task<object> t1 = epaTest1.ToTask();
Task<object> t2 = epaTest2.ToTask();

// wait for both t1 and t2 to finish
Task.WaitAll(t1, t2);

// create and start t3 task
Task<object> t3 = epaTest3.ToTask();

t3.Wait();  

Tasks t1 and t2 are run in parallel. Task t3 is scheduled to run only after t1 and t2 have completed.

The scheduling effect above would be much more difficult to achieve without the TPL functionality.

New .NET 4.5 async and await Features

.NET 4.5 async and await features simplify the asynchronous programming even further. They basically allow writing and debugging an async program almost as if it is a usual synchronous program.

In order to run the samples from this section, you need Visual Studio 2012.

Simple async-await Test

Take a look at Program.cs file under AwaitTest program:

static void Main(string[] args)
{
    RunIt();

    Console.WriteLine("End of Main");
    Console.ReadLine();
}

static async void RunIt()
{
    Task t = new Task
    (
        () =>
        {
            Thread.Sleep(3000);

            Console.WriteLine("Ended the task");
        }
    );

    t.Start();

    await t;

    Console.WriteLine("After await");
}

Notice that there is a method RunIt() called from Main(). Why can't we test await keyword within the Main() method itself? The reason is that any method that has await keyword in it should be declared with async keyword as a method that possibly introduces some asynchronous processing, while Main() is a special method that cannot be marked with async keyword.

Note, that when you run the code, the string "End of Main" is printed before "Ended the task". This means that during the "waiting" period, the main thread is not blocked - it continues and would have completed the program if it was not for the Console.ReadLine() call at the end of the Main() method. This is a major difference between await and Task.Wait() method. Indeed, change await t; line to t.Wait() and remove async keyword from RunIt method's declaration. After that, when you run the code, first "Ended the task" message will be printed and only later "End of Main" - meaning that t.Wait() is blocking the main thread until the Task is completed.

The execution context assigns a thread for the code placed after await keyword to be executed once waiting is completed.

Note that we can change the return type of RunIt() method to Task without returning anything from the method itself. Compiler will generate the Task object for the method itself, since the method is declared with async keyword. The Main method can get the task and wait on it:

static void Main(string[] args)
{
    Task t = RunIt();

    t.Wait();

    Console.WriteLine("End of Main");
    Console.ReadLine();
}

static async Task RunIt()
{
    Task t = new Task
    (
        () =>
        {
            Thread.Sleep(3000);

            Console.WriteLine("Ended the task");
        }
    );

    // start the task
    t.Start();

    await t;
    Console.WriteLine("After await");
}  

Because of calling Wait() method on the Task object returned by RunIt() method, the Main() method blocks until asynchronous processing is completed within RunIt() and "End of Main" string is printed after "Ended the Task".

Async-Await Tests with Return Values

Code for AwaitWithReturnsTest project is very similar to that considered above, except that the Task that we are waiting for, returns a string result and correspondingly we use string result = await t; to assing the result of the Task to a string:

static void Main(string[] args)
{
    Task<string> runItTask = RunIt();

    Console.WriteLine("End of Main");
    Console.ReadLine();
}

static async Task<string> RunIt()
{
    Task<string> t = new Task<string>
    (
        () =>
        {
            Thread.Sleep(3000);

            Console.WriteLine("Ended the task");

            return "This is task";
        }
    );

    // start the task
    t.Start();

    // get the result from the task - analogous to 
    // calling t.Result (without the thread blocked, of course)
    string result = await t;
    Console.WriteLine("After await, result = '" + result + "'");
 
   return result;
}  

Note, that our RunIt() function returns result of type string, while it is decrlared to return a Task<string>. The change of the return type happens due to the async keyword in front of the method declaration, which wraps whatever is returned by the function within a Task. The returned Task<string> can be used to block the Main() thread execution until the Task finishes or to use await keyword on it higher up the function stack.

Async-Await and Exceptions

Take a look at AWaitWithException project. File Program.cs is almost the same as that of our AwaitTest project except that there is an exception thrown within the Task. This exception is caught by the try-catch block around await t; line and the exception message is printed:

static async void RunIt()
{
    Task t = new Task
    (
        () =>
        {
            Thread.Sleep(3000);
            throw new Exception("This is an exception thrown from a Task");
            Console.WriteLine("Ended the task");
        }
    );

    // start the task
    t.Start();

    try
    {
        await t;
    }
    catch (Exception e)
    {
        Console.WriteLine("Caught Exception '" + e.Message + "'");
    }

    Console.WriteLine("After await");
}  

Note that await keyword unwraps one level of the AggregateException so that in the case above, we catch the original Exception and not the AggregateException object. This is another way in which async programming becomes more like the usual synchronous one.

Usage Patterns for Task and Async-Await Functionality

In this section I describe several important cases when the TPL and async-await functionality comes very handy.

Remote Service Calls using .NET 4.5

Most financial applications collect data from many different disjoint sources - there are usually some newer databases, older databases, some data feeds etc. A single request to read or modify financial data can include calls to multiple services (whether such request is made from a UI or web client or from some middleware). Some of such service calls might need to be performed in parallel in order to get all the required replies earlier, some of the service calls might be triggered only after the results of other service calls have arrived, since their input might depend on them. Task would be an ideal way of representing such service calls since we can easily run multiple Task in parallel and wait on a number of Task objects before starting other Tasks.

Services that provide Web Service interface can be added to and accessed from .NET application as Service References (the services themselves can be coded in Java or C# or anything else - it does not matter as long as they are legal Web Services). In .NET 4.5 the Service References can be configured to be accessed asynchronously as Tasks.

Solution CallingServiceViaTasksTest consists of two projects:

  • MockService - contains a very simple WCF service mocking a number of service calls returning financial data.
  • CallingServiceViaTasksTest - contains the test client that gets mock financial data and prints it to the console

The service contains three methods (operations) available remotely:

  • GetSecurityName - given a security id, it will return a security name - in fact my mock implementation will return the same name "MSFT" for anything you pass to it.
  • GetExchangesSecurityTradedOn - given security id, it will return the names of the exchanges where this security is traded
  • GetSecurityExchangeData - given the security id and the exchange name it returns the price of the security on that exchange.

Here is the very simple implementation of the mock service:

[ServiceContract]
public class MockFinancialDataService
{
    [OperationContract]
    public string GetSecurityName(string securityID)
    {
        return "MSFT";
    }

    [OperationContract]
    public string[] GetExchangesSecurityTradedOn(string securityID)
    {
        return new string[] { "NASDAQ", "NYCE" };
    }

    [OperationContract]
    public double GetSecurityExchangeData(string securityID, string exchangeName)
    {
        if (exchangeName == "NASDAQ")
            return 20.00;

        else if (exchangeName == "NYCE")
            return 30.00;

        return -1.0;
    }
}  

Assuming that you are building this project from scratch, you can add a service reference to the client project by right-mouse clicking the References under the client project name within solution explorer and choosing "Add Service Reference". Set "http://localhost:20575/MockFinancialDataService.svc" to be the service URL, then click the button "Advanced" and make sure that "Allow generation of asynchronous operations" checkbox is clicked and the option "Generate task-based operations" is chosen under this checkbox:

Image 1

If you cannot select "Generate task-based operations" - it means that your client project is not using .NET 4.5 (most likely it uses .NET 4.0) and you need to change your framework and then reconfigure the service reference.

This client code is also very neat and simple (due to the TPL and async-await functionality):

public static async Task RunIt()
{
    MockFinancialDataServiceClient client = new MockFinancialDataServiceClient();

    // we get the security name and echanges it is traded on in parallel at
    // the first stage. 
    Task<string> securityNameTask = client.GetSecurityNameAsync("1234");
    Task<string[]> exchangesTask = client.GetExchangesSecurityTradedOnAsync("1234");

    // we wait for the first stage tasks to complete
    await Task.WhenAll(securityNameTask, exchangesTask);

    string securityName = securityNameTask.Result;
    foreach (string exchangeName in exchangesTask.Result)
    {
        double price = await client.GetSecurityExchangeDataAsync("1234", exchangeName);

        Console.WriteLine(securityName + "\t" + exchangeName + "\t" + price);
    }
}  

We request the security name and the exchanges on which it trades in parallel. Then wait until both produce results. After that, for each of the returned exchanges, we request the price information and print out the security name, the exchange name and the price to the console.

Now, try to imagine implementing the code above without TPL functionality. Instead of one little neat function, you would have at least two functions (or lambdas) per remote call. On top of that, you would have to add a lot of code for implementing the functionality to wait for the two first asynchronous calls to complete before calling the GetSecurityExchangeDataAsync remote method.

Remote Service Calls using Older API

If you company has not moved to .NET 4.5 yet, you can still use write the Task wrappers around the asynchronous service calls. Solution CallingServicesViaTasksOlderAPITest shows how to do it. It consists of the WCF MockService project - the same as in the previous sample and the client project.

When the service reference is configured to generate async service calls, .NET 4.0 and earlier .NET versions will generate methods to call the service in two different ways - Event-based Asynchronous Pattern (EAP) and Asynchronous Programming Model (APM). We took a look at EAP above - it creates a method to start an asynchronous operation and a callback registered to fire when the async operation completes.

APM pattern has two methods - one to start an async operation, the other to block until the operation completes. In case of our remote services - the APM methods to start an operation start with "Begin" prefix, while those that block till the operation completes start with "End" prefix, e.g. service operation GetSecurityName results in two APM methods: BeginGetSecurityName to start the operation and EndGetSecurityName to block till operation completes.

We use the APM pattern to create Task wrappers for calling async services - it can be achieved even easier for APM pattern than for EAP one by using Task.Factory.FromAsync<TResult>(...) function. Here is our client code:

static void Main(string[] args)
{
    MockFinancialDataServiceClient client = new MockFinancialDataServiceClient();

    Task<string> getNameTask = Task<string>.Factory.FromAsync<string>
    (
        client.BeginGetSecurityName,
        client.EndGetSecurityName,
        "1234", // argument to the async call
        null // state (not needed)
    );


    Task<string[]> getExhcangesTask = Task<string[]>.Factory.FromAsync<string>
    (
        client.BeginGetExchangesSecurityTradedOn,
        client.EndGetExchangesSecurityTradedOn,
        "1234",
        null
    );

    Task.WaitAll(getNameTask, getExhcangesTask);

    string securityName = getNameTask.Result;

    string[] exchanges = getExhcangesTask.Result;

    foreach (string exchangeName in exchanges)
    {
        Task<double> priceTask = Task<double>.Factory.FromAsync<string, string>
        (
            client.BeginGetSecurityExchangeData,
            client.EndGetSecurityExchangeData,
            "1234",
            exchangeName,
            null
        );

        Console.WriteLine(securityName + "\t" + exchangeName + "\t" + priceTask.Result);
    }
} 

Task.Factory.FromAsync<TResult>(...) method creates a task around two APM methods. After these arguments, you need to pass the input arguments to the Begin... APM method. The last argument to FromAsync is state variable, which we do not use and pass as null. FromAsync has a bunch of overrides allowing to enter up to three arguments to the Begin APM function. If, however, the Begin function has more than three arguments, you can always wrap it within a lambda expression or a delegate that takes less than three arguments and pass this lambda expression to FromAsync.

Note, that our previous example of a service call (using .NET 4.5 functionality) will not block the thread it runs on, e.g. the UI thread in WPF application. The current example of using older .NET functionality, however, does block the current thread by its Wait or WaitAll methods. So, if you do not want the thread to be blocked by it, you can always start it on a different thread, e.g. by making it continue a Task running on its own thread.

Wrapping a BackgroundWorker in a Task

Having Task functionality made BackgroundWorker functionality largerly obsolete - you can achieve whatever you need using a Task instead of a BackgroundWorker. Still there might be some reason you want to use BackgroundWorker functionality on the team - whether because your legacy code uses it or because most of your team members or your boss love it and understand it better than the newer Task functionality.

Here, we show, how to wrap the BackgroundWorker within Task so that you could gain all the flexibility of Task functionality for BackgroundWorker based code.

Solution BackgroundWorkerTaskWrap shows how to wrap a background worker within a Task.

The task wrapping code is located under BackgroundWorkerUtils.ToTask() extension function. It is TaskCompletionSource based, so the resulting task is started during the conversion. It provides interface for Task cancellation and progress reporting:

public static Task<object> ToTask
(
    this BackgroundWorker backgroundWorker, 
    CancellationTokenSource cancellationTokenSource = null,
    IProgress<object> progress = null
)
{
    TaskCompletionSource<object> taskCompletionSource =
        new TaskCompletionSource<object>(TaskCreationOptions.AttachedToParent);

    if (cancellationTokenSource != null)
    {
        // when the task is cancelled, 
        // trigger CancelAsync function on the background worker
        cancellationTokenSource.Token.Register
        (
            () =>
            {
                if (backgroundWorker.WorkerSupportsCancellation)
                    backgroundWorker.CancelAsync();
            }
        );
    }

    if (progress != null)
    {
        backgroundWorker.ProgressChanged += (sender, progressChangedArgs) =>
            {
                progress.Report(progressChangedArgs.ProgressPercentage);
            };
    }

    RunWorkerCompletedEventHandler onCompleted = null;

    onCompleted = (object sender, RunWorkerCompletedEventArgs e) =>
        {
            backgroundWorker.RunWorkerCompleted -= onCompleted;
                    
            if (e.Cancelled)
            {
                // if the background worker was cancelled,
                // set the Task as cancelled.  
                taskCompletionSource.SetCanceled();
                taskCompletionSource.SetException(new OperationCanceledException());
            }
            else if (e.Error != null)
            {
                taskCompletionSource.SetException(e.Error);
            }
            else
            {
                taskCompletionSource.SetResult(e.Result);
            }
        };

    backgroundWorker.RunWorkerCompleted += onCompleted;

    backgroundWorker.RunWorkerAsync();

    return taskCompletionSource.Task;
}

And here is an example of how to use the functionality from Program.cs file:

    static void Main(string[] args)
    {
        BackgroundWorker backgroundWorker1 = new BackgroundWorker();

        backgroundWorker1.DoWork += (sender, e) =>
            {
                Thread.Sleep(2000);
                Console.WriteLine("this is backgroundWorker1");
            };

        BackgroundWorker backgroundWorker2 = new BackgroundWorker();
        backgroundWorker2.DoWork += (sender, e) =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("this is backgroundWorker2");
        };

        BackgroundWorker backgroundWorker3 = new BackgroundWorker();
        backgroundWorker3.DoWork += (sender, e) =>
        {
            Thread.Sleep(1000);
            Console.WriteLine("this is backgroundWorker3");
        };

        // schedule backgroundWorker1 and backgroundWorker2 to run in parallel
        Task<object> t1 = backgroundWorker1.ToTask();
        Task<object> t2 = backgroundWorker2.ToTask();

        Task.WhenAll(t1, t2).Wait();

        // schedule backgroundWorker3 to run when both backgroundWorker1 
        // and backgroundWorker2 are completed
        Task<object> t3 = backgroundWorker3.ToTask();

        t3.Wait();
    }
}

In the sample above we schedule two background workers to run in parallel and the third one to start when two first background workers completed their run.

Wrapping WPF Storyboards in Tasks

I always wanted be able to combine multiple storyboards, so that some of them would run in parallel and then when they finish, some other animations would start. Task functionality is ideal for making this happen.

The storyboard sample is located under WrappingStoryboardsInTasks project.

The XAML file of the project contains two rectangles blue and three Storyboard objects. First two storyboard rotate correspondingly blue and red rectangle, while the 3rd one increases their width.

Image 2

On pressing "Start Animation" button we want to start the first two Storyboards and when they both finish, we want to start the 3rd one. On top of that, once the StartAnimationButton button is clicked, we want to disable it and re-enable it once the last animation stops in order to prevent starting the new storyboard run in the middle of the previous one.

Here is how simple the code becomes when we use the TPL and async-await functionality:

async void StartAnimationButton_Click(object sender, RoutedEventArgs e)
{
    StartAnimationButton.IsEnabled = false;

    Storyboard storyboard1 = (Storyboard)MyGrid.Resources["Storyboard1"];
    Storyboard storyboard2 = (Storyboard)MyGrid.Resources["Storyboard2"];
    Storyboard storyboard3 = (Storyboard)MyGrid.Resources["Storyboard3"];

    Task<object> t1 = storyboard1.ToTask();
    Task<object> t2 = storyboard2.ToTask();

    // wait for storyboard1 and storyboard2 to finish
    await Task.WhenAll(t1, t2);

    // start storyboard3
    await storyboard3.ToTask();

    StartAnimationButton.IsEnabled = true;
}  

The code results in a number of callbacks - one to start storboard3, another to enable the StartAnimationButton, but it all looks like a linear synchronous code due to the TPL and async-await magic.

The code for converting Storyboards to Tasks is placed into StoryboardToTask static class as ToTask(...) extension method:

public static Task<object> ToTask
(
    this Storyboard storyboard, 
    CancellationTokenSource cancellationTokenSource = null
)
{
    TaskCompletionSource<object> taskCompletionSource =
        new TaskCompletionSource<object>(TaskCreationOptions.AttachedToParent);

    if (cancellationTokenSource != null)
    {
        // when the task is cancelled, 
        // Stop the storyboard
        cancellationTokenSource.Token.Register
        (
            () =>
            {
                storyboard.Stop();
            }
        );
    }

    EventHandler onCompleted = null;

    onCompleted = (object sender, EventArgs e) =>
    {
        storyboard.Completed -= onCompleted;

        taskCompletionSource.SetResult(null);
    };

    storyboard.Completed += onCompleted;

    // start the storyboard during the conversion.
    storyboard.Begin();

    return taskCompletionSource.Task;
}  

Note that we use TaskCompletionSource to create a Task wrapper around the Storyboards, so we should be starting the Storyboard during the conversion.

Storyboards themselves consist of animation objects. Eventually (time permitting) I would like to use Task objects to wrap individual animations and built a new type of Storyboard-like objects having different animation starting based on when the previous animations finished instead of being time based. This would result in the animation objects that are easier to build and control.

Summary

In this article we provided information on using TPL and async-await features for creating asynchronous functionality that is almost as simple as the synchronous code. We showed how to apply these concepts to sending and processing numerous service requests, scheduling multiple BackgroundWorkers and Storyboards.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect AWebPros
United States United States
I am a software architect and a developer with great passion for new engineering solutions and finding and applying design patterns.

I am passionate about learning new ways of building software and sharing my knowledge with others.

I worked with many various languages including C#, Java and C++.

I fell in love with WPF (and later Silverlight) at first sight. After Microsoft killed Silverlight, I was distraught until I found Avalonia - a great multiplatform package for building UI on Windows, Linux, Mac as well as within browsers (using WASM) and for mobile platforms.

I have my Ph.D. from RPI.

here is my linkedin profile

Comments and Discussions

 
GeneralMy vote of 5 Pin
gizi30-Apr-13 21:51
gizi30-Apr-13 21:51 
GeneralRe: My vote of 5 Pin
Nick Polyak1-May-13 3:37
mvaNick Polyak1-May-13 3:37 
GeneralRe: My vote of 5 Pin
Nick Polyak1-May-13 10:49
mvaNick Polyak1-May-13 10:49 
GeneralRe: My vote of 5 Pin
gizi1-May-13 17:41
gizi1-May-13 17:41 
GeneralRe: My vote of 5 Pin
Nick Polyak1-May-13 17:52
mvaNick Polyak1-May-13 17:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.