Tasks and the Event-based Asynchronous Pattern

Tasks and the Event-based Asynchronous Pattern

  • Comments 15

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, Action unregisterHandler)
{
    if (e.UserState == tcs)
    {
        if (e.Cancelled) tcs.TrySetCanceled();
        else if (e.Error != null) tcs.TrySetException(e.Error);
        else tcs.TrySetResult(getResult());
        if (unregisterHandler != null) unregisterHandler();
    }
}

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, null);
    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!

Leave a Comment
  • Please add 5 and 5 and type the answer here:
  • Post
  • 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.

  • 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.

  • 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?

  • 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.

  • 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.

  • 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.

  • 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.

  • MichaelGG, thanks for the comment.

  • 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?

  • 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.

  • 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).

  • I belive that there is a bug in the example above, in lines (final version of DownloadDataTask):

    TransferCompletionToTask(tcs, e, () => e.Result);

    webClient.DownloadDataCompleted -= handler;

    because second line is executed even if

    if (e.UserState == tcs)

    (in TransferCompletionToTask) is false, that causes handler unregistration prior "right" DownloadDataCompleted is raised.

  • Great catch, holatom.  You're correct.  I'll fix the issue.

  • What I'm wondering is if EAP is no longer recommended to be used when you are developing a library of your own? i.e. I created my own library and encapsulated all the functionality in it so that others could use it easily in an asynchronous manner. Is the new recommendation from Microsoft to not use EAP anymore for this and instead use TPL? I'm wondering if I should update my library and get rid of the use of EAP.

  • Hi Jon-

    EAP is still an ok pattern to utilize if your main goal is to enable a UI thread to easily kick off a single asynchronous operation which will complete back on the UI thread; if that matches how you expect your library to be utilized, by all means, stick with it.

Page 1 of 1 (15 items)