Welcome to MSDN Blogs Sign in | Join | Help

News

  • These postings are provided "AS IS" with no warranties and confer no rights. All code and tools presented are done so under the Microsoft Public License.
Tasks and the Event-based Asynchronous Pattern

As has been discussed previously, one of the new features in the Task Parallel Library is TaskCompletionSource<TResult>, which enables the creation of a Task<TResult> that represents any other asynchronous operation. 

There are a wide variety of sources in the .NET Framework for asynchronous work.  One comes from components that implement the Asynchronous Programming Model (APM) pattern, which we discussed here.  Another includes types that implement the Event-based Asynchronous Pattern (EAP).  For some synchronous method Xyz, the EAP provides an asynchronous counterpart XyzAsync.  Calling this method launches the asynchronous work, and when the work completes, a corresponding XyzCompleted event is raised. (That’s an oversimplification of the pattern, but it provides a grounding.)

For many situations, the EAP is quite straightforward and simple to use.  However, there are cases where you’d like to be able to do things like join across multiple EAP asynchronous invocations, such as to download three different web pages asynchronously, and only when all three have completed do something else.  Tasks in .NET 4.0 make this kind of operation easy, through Task.Factory.ContinueWhenAll (or ContinueWhenAny if you want to do something when any one of the items completes rather than when all of them do).  However, in order to use these methods, you need Tasks, thus to use them with components that implement the EAP, you need to create Tasks from EAP.  TaskCompletionSource<TResult> can be used to do exactly that.

First, let’s create two small helper functions that we can reuse over and over for multiple EAP-to-Task implementations:

private static TaskCompletionSource<T> CreateSource<T>(object state)
{
    return new TaskCompletionSource<T>(
        state, TaskCreationOptions.DetachedFromParent);
}

private static void TransferCompletion<T>(
    TaskCompletionSource<T> tcs, AsyncCompletedEventArgs e,
    Func<T> getResult)
{
    if (e.UserState == tcs)
    {
        if (e.Cancelled) tcs.TrySetCanceled();
        else if (e.Error != null) tcs.TrySetException(e.Error);
        else tcs.TrySetResult(getResult());
    }
}

The first helper, CreateSource, simply creates an instance of TaskCompletionSource<T> with the right type and settings.  I’ve included TaskCreationOptions.DetachedFromParent under the assumption that tasks created for the EAP pattern aren’t necessarily meant to participate in parent/child relationships, but you could certainly change this if your scenarios needed that functionality.

The second helper, TransferCompletion, will be used whenever an EAP event signals completion of an asynchronous operation.  It takes the AsyncCompletedEventArgs provided through the XyzCompleted event and uses it to determine whether the operation completed due to cancellation or due to an unhandled exception.  Both of those pieces of data are standard to the base AsyncCompletedEventArgs class. However, each EAP operation typically comes with its own type derived from AsyncCompletedEventArgs: to be able to use this one helper function with any of those to mine the result of the operation, TransferCompletion also accepts a Func<T> that will return the results from the derived instance. 

With those two helpers, writing a Task wrapper for an EAP operation is a cinch.  Consider System.Net.WebClient, which provides support for downloading and uploading to and from URIs, with methods like DownloadData.  We can write a Task-based wrapper for DownloadData in just a few lines (in this case, as an extension method):

public static Task<byte[]> DownloadDataTask(
    this WebClient webClient, Uri address)
{
    var tcs = CreateSource<byte[]>(address);
    webClient.DownloadDataCompleted +=
        (sender, e) => TransferCompletion(tcs, e, () => e.Result);
    webClient.DownloadDataAsync(address, tcs);
    return tcs.Task;
}

We first create a TaskCompletionSource<byte[]> whose Task<byte[]> will be returned from the method.  We then register with the DownloadDataCompleted event handler, such that when the event is raised, the downloaded data (or exception or cancellation information) will be transferred to the returned Task.  And then we start the operation.  Piece of cake.

One interesting thing to note about the EAP is that some implementations support multiple asynchronous operations concurrently on the same instance.  In such cases, a user-supplied token is needed to correlate the asynchronous invocations to the asynchronous completions (something that’s provided in Task implicitly by having a Task reference returned from and serving as a reference for an asynchronous operation).  For that purpose, we pass the created TaskCompletionSource<TResult> as the user-supplied token, and in TransferCompletion, we only transfer the results if the token received matches the target completion source.  If it doesn’t match, this is an event completion for another operation and should be ignored.

One potential issue with the previously shown implementation (and with the EAP pattern in general) is that, if they’re used over and over, the XyzCompleted event handlers will start to build up.  Delegates are being registered with the event but not released.  To fix that, we can also remove the event handler in the delegate handling the event.  For example, DownloadDataTask can be rewritten as:

public static Task<byte[]> DownloadDataTask(
    this WebClient webClient, Uri address)
{
    var tcs = CreateSource<byte[]>(address);

    DownloadDataCompletedEventHandler handler = null;
    handler = (sender, e) => {
        TransferCompletionToTask(tcs, e, () => e.Result);
        webClient.DownloadDataCompleted -= handler;
    };

    webClient.DownloadDataCompleted += handler;
    try { webClient.DownloadDataAsync(address, tcs); }
    catch {
        webClient.DownloadDataCompleted -= handler;
        tcs.TrySetCanceled();
        throw;
    }

    return tcs.Task;
}

The Beta 1 samples available at http://code.msdn.microsoft.com/ParExtSamples already include Task-based extensions for WebClient, as well as extensions for other EAP implementations like SmtpClient and Ping.  Download and enjoy!

Posted: Friday, June 19, 2009 11:43 AM by toub

Comments

Barry Kelly said:

You neatly demonstrate all the horribleness of the EAP style, when contrasted with the easier to use APM.

I must ask, though, how do you unsubscribe from the *Completed events using your helpers?

I was most frustrated when EAP turned up in .NET 2.0; EAP was chosen ahead of APM for web-service proxies, IIRC. APM was used in one of the earlier CTPs, and was a joy to use, but then EAP went into the RTM version and broke all my code. Demultiplexing stateful events invoked upon completion of stateless async method calls? Yuck.

# June 19, 2009 8:27 PM

toub said:

Hi Barry-

Thanks for the comments.  Regarding unsubscribing, the implementations in the Beta 1 samples (and the last example shown in the blog post itself) unregister from the *Completed event automatically when the operation completes.  Are you looking to unregister prior to the operation completing?  If so, there are a variety of solutions.  One would be, prior to returning from the helper, to register a handler with the Task's CancellationToken such that when the Task has cancellation requested, it unregisters from the *Completed event and transitions the Task into the Canceled state.

I hope that helps.

# June 19, 2009 8:36 PM

Aleksander Polak said:

This caught my attention:

if (e.Cancelled) tcs.TrySetCanceled();

Although English is not my native language, I believe the form "Canceled" should be used in both cases. (As far as I remember this was even mentioned in "Framework Design Guidelines".) At the very least the naming should be consistent. Yes, both forms can be found in the BCL today, but not within a single API.

Any plans to fix that before you ship the bits?

# June 20, 2009 11:12 AM

Mike said:

Hi

I have the following Problem.

I have a few Validation Tasks. All of them get their Data from a "Linq to XML" Source. I am using "AsParrallel" for this Linq Ressource.

Now i want to run all these Task parrallel and i am interested only in the following:

- If all of this Taskes finish successful then move on.

- If one of this Tasks throw an Exception then immediatelly kill all other tasks and give back the exception of this task which has thrown it.

Is this Doable with the new Task Library.

# June 20, 2009 6:36 PM

MichaelGG said:

I don't know if I'll ever understand why MS added the silly event async pattern :(. With APM we can easily define a generic wrapper for all sorts of methods, without needing detailed wiring up code.

# June 21, 2009 12:20 AM

toub said:

Aleksander-

You're correct that Canceled (one L) is the right spelling to be used, per the latest Framework guidelines, and it's why the Task Parallel Library uses that spelling.  The "e.Cancelled" you're seeing is not coming from TPL, it's coming from the AsyncCompletedEventArgs class which shipped as part of the .NET Framework 2.0 to support the Event-based Asynchronous Pattern (EAP).  This post is simply showing how to take classes that implement the EAP pattern using that API, and get them to interoperate with Tasks.

Thanks for the suggestion, though.

# June 21, 2009 1:31 PM

toub said:

Mike, you can certainly do this with tasks, taking advantage of ContinueWhenAll, ContinueWhenAny, and cancellation.  You might also be able to achieve the same thing by extending your PLINQ solution to first iterate through all of the XML files, since PLINQ will try to stop executing a query as fast as possible when an exception is thrown from elsewhere in the query.

# June 21, 2009 1:37 PM

toub said:

MichaelGG, thanks for the comment.

# June 21, 2009 1:39 PM

Joe Albahari said:

What exactly was the design goal of the EAP? Was it simply to achieve parallel execution - or was it intended as a wrapper around APM?

# June 29, 2009 5:19 AM

toub said:

EAP was designed to simplify one-off asynchronous operations that are initiated from and need to conclude on the UI thread (or, more generally, in some SynchronizationContext).  With a type like WebClient, in a client app you can asynchronously download some data and handle the downloaded data on the UI thread.  Or in ASP.NET, you can implement an async page that uses some asynchronously downloaded data, and again achieve that relatively easily using a type like WebClient that implements the EAP pattern.  The pattern starts to lose steam, however, when you need to compose multiple asynchronous operations, or when you don't know at async operation launch time what you want to do with the result of the operation when it's complete, etc.  In short, it can be quite useful for some scenarios, but puts up roadblocks for other more complicated ones.

In any event, the purpose of this blog post was to demonstrate how Tasks can be used to represent existing EAP implementations, just as Tasks can be used to represent existing APM implements.

# June 29, 2009 10:44 AM

Charles Prakash Dasari said:

Though EAP was introduced for one-off async operations like you mentioned above, MSDN documentation goes far and beyond to recommend EAP over APM. I find APM more convenient than EAP particularly for the following reasons:

1) Exception handling

2) Explicit call to EndXXXX method

3) No multiple listeners business (in EAP you could have multiple listeners for the completion event. While this may be an interesting side-affect, it is still a potential candidate for bugs especially when the effect is not foreseen by the developer of the EAP functions than the one who consumes).

# July 31, 2009 5:26 AM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

  
Enter Code Here: Required

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker