Tasks and the APM Pattern

Tasks and the APM Pattern

  • Comments 15

The Asynchronous Programming Model (APM) in the .NET Framework has been around since .NET 1.0 and is the most common pattern for asynchrony in the Framework.  Even if you’re not familiar with the name, you’re likely familiar with the core of the pattern.  For a given synchronous operation Xyz, the asynchronous version manifests as BeginXyz and EndXyz:  BeginXyz starts the operation and the EndXyz method joins with it (completes it).  There are several mechanisms by which the results can be joined with, such as by polling for completion using the IsCompleted property on the IAsyncResult returned from BeginXyz, blocking until the operation has completed by waiting on the IAsyncResult’s AsyncWaitHandle, simply calling EndXyz and passing it the IAsyncResult (which will block until the operation has completed), or passing to BeginXyz a callback which will be invoked when the operation has completed: that callback should then call EndXyz to retrieve the results.

This pattern is so common that we’ve opted to incorporate it as a first-class citizen into the Task Parallel Library.  One way we’ve done this is by having the Task class itself implement IAsyncResult: this means that Task can be used as the core of a Begin/End implementation, easing the implementation for common scenarios.  One feature new to .NET 4 Beta 1 since previous CTPs, however, is that we now support the inverse, easy creation of tasks from an implementation of the APM pattern.  This new functionality shows up through the FromAsync methods on TaskFactory and TaskFactory<TResult>.

Under the covers, FromAsync utilizes the TaskCompletionSource<TResult> type. Let’s say you wanted to create a Task that represents an asynchronous read on a Stream.  You could write something like the following:

public static Task<int> ReadTask(this Stream stream,
    byte [] buffer, int offset, int count, object state)
{
    var tcs = new TaskCompletionSource<int>();
    stream.BeginRead(buffer, offset, count, ar =>
    {
        try { tcs.SetResult(stream.EndRead(ar)); }
        catch(Exception exc) { tcs.SetException(exc); }
    }, state);
    return tcs.Task;
}

You could of course write a similar wrapper for every Begin/End pair you wanted to utilize, but that could get tedious and error-prone.  To solve that, we’ve provided the FromAsync method, which takes advantage of the common structure of the APM pattern, along with delegate inference, to provide generic overloads that work with most APM implementations. 

For example, let’s say I have a Stream and a byte buffer, and I want to read from that stream into the buffer.  Synchronously, I could do something like:

int bytesRead = stream.Read(buffer, 0, buffer.Length);

Creating a Task that does the same thing asynchronously:

Task<int> bytesRead = Task<int>.Factory.FromAsync(
    stream.BeginRead, stream.EndRead, buffer, 0, buffer.Length, null);

Under the covers, we follow a pattern very similar to the one shown earlier as a specific implementation for Stream.Read. Combine this support with the ability to do Task.WaitAll, Task.WaitAny, Task.Factory.ContinueWhenAll, and Task.Factory.ContinueWhenAny, and you can achieve some very useful coordination functionality in very little code.

Of course, we don’t have any magic at our disposal; all of this functionality is available through standard .NET libraries.  That means that we achieve the above with a method with the following signature:

public Task<TResult> FromAsync<TArg1, TArg2, TArg3>(
    Func<TArg1, TArg2, TArg3, AsyncCallback,
        object, IAsyncResult> beginMethod,
    Func<IAsyncResult, TResult> endMethod,
    TArg1 arg1, TArg2 arg2, TArg3 arg3,
    object state);

You’ll notice then that this overload is coded specifically for APM implementations that take three input parameters (of types TArg1, TArg2, and TArg3).  The vast majority of the APM implementations in the .NET Framework take three or less input parameters, with only a trickling accepting more than that.  As such, we’ve included overloads in the Task Parallel Library that follow this pattern for up to and including three parameters.  Of course, as with all corner cases, there will certainly be scenarios that require usage with more than three parameters, or with slightly different forms.  For those cases, we’ve also added overloads that accept an IAsyncResult as the first parameter, rather than accepting a beginMethod delegate.  This way, you can pass in any IAsyncResult you want, and we’ll call back to the endMethod when we find that the IAsyncResult has completed.  This approach typically isn’t as efficient as the beginMethod approach, but it can be quite handy in a pinch.

Leave a Comment
  • Please add 4 and 3 and type the answer here:
  • Post
  • Very nice, those IAsyncResults will be much better handled this way!

    Out of curiosity however: How come you didn't go with 4 arguments, matching the max number of arguments supported by the Func delegate..?

  • Glad you like it!

    Func<> has actually been expanded in .NET 4 such that there are variants which take up to 16 parameters.  Something like 95% of all APM implementations in the core Framework use three parameters or less, and we had to draw the line somewhere.  However, if you do have more than three, you still have options.  One is that you can relatively easily implement your own variants of this with as many parameters as you like (and with the FromAsync methods being instance methods on TaskFactory, you can even make your methods extension methods so they show up in IntelliSense and the syntax along with the rest of the built-in overloads).  Another is that the overloads we provide which accept an IAsyncResult as the first parameter rather than a beginMethod delegate allows you to work with anything that returns an IAsyncResult, including begin methods that accept any number of parameters.

  • Bridge from previous pratices are always good thinks.

    As I understans, Task instance returned by FromAsync is already started. Do you plan to provide

  • As I understand, Task instance returned by FromAsync has already been started.

    I find this name not as descriptive as it should be. "FromAsync" name doesn't says starting process has already begin.

    Do you plan to provide a second implementation where created Task have not been started?

  • Hi Olivier-

    Thanks for the feedback. We don't currently have plans to provide a version where the asynchronous operation hasn't been started when the method returns, though it's certainly something we can look into doing.  In what scenarios would this be useful to you?

  • Hi Stephen,

    First, I like  self descriptive names and I really think FromAsync miss the already started aspect (Task.Wrap and Task.WrapAndStart or something like this should be better).

    Second, I always separates object creation and execution start. It's not same stuff.

    Third, I work on a StateMachine engine where state activities must be described during initialization of state machine. Tasks can be used to implement states activities but Start process must be done only on state entry during execution step (long away from initialization).

    In fact, the initial problem is having your Task implements IAsyncResult. When you start a standard BeginXyz APC, you only get IAsyncResult implementation instance when process have already been started. IAsyncResult is a kind of token describing  a currently running asynchronious execution. But Task can be started after construction. So, during time time between construction and Start, your Task is not an IAsyncResult.

  • Hi Olivier- Thank you for your feedback.

  • PingBack from http://www.vishwatech.com/globalnews/?p=1714

  • Hmm... in addition to Begin and End async methods, it might be nice if you added helpers for synchronous methods.

    Twisted Python does something like this, and it's very handy. Oftentimes you have a method that could be asynchronous, but is synchronous in some instances. In those cases, you'd (with TPL) end up doing

    var f = Future<blah>.Create();

    f.Value = value;

    return f;

    It'd be nice if there were a helper, such that I could just return Future<blah>.Succeed(value); or in the case of setting Exception, Future<blah>.Fail(value);

    Look at Twisted Python's Deferred for more inspiration. :)

  • Hi James-

    Thanks for the suggestion.  We've considered adding such a helper, e.g. Task.Factory.FromResult(value) and Task.Factory.FromException(exc), and may do so at some point in the future.  Could you elaborate on the scenarios where this would be useful to you?

    You can also write these yourself as extension methods (in fact, you'll find such extension methods in the Beta 1 samples at http://code.msdn.microsoft.com/ParExtSamples), e.g.

    public static Task<TResult> FromResult<TResult>(this TaskFactory factory, TResult result)

    {

       var tcs = new TaskCompletionSource<TResult>();

       tcs.SetResult(result);

       return tcs.Task;

    }

    Thanks.

  • Scenarios... Hmm. Sure thing. Suppose you have a server with some methods that can return from immediately, and some that require file access, database access, etc.

    Now, in Twisted Python, there's a function called defer.maybeDeferred that will take a normal value or a Deferred (essentially a Future<T> in your case), and if it's a normal value call defer.succeed on the value to make it a Deferred. Since .NET is strongly typed this is not necessary. Instead the server would pass back a Future<T>.

    Then, the method calling the server's request function will receive the Future<T> back, and attach callbacks that take in the response and encode it. The encoder returns a Future<T>. The I/O level then attaches callbacks to send it to the other side. Each level modifies the return value.

    Anyway, what the .succeed/.fail feature does is allow this whole process, in the event that there's nothing to wait on, to occur without any switching or scheduling at all. .callback (.NET .ContinueWith) would execute immediately, since the Future<T> returned is already finished. This is a rather common case.

    And yeah, right now I have it inside a FutureHelper I wrote (using the CTP, there is no Factory, so nowhere to put an extension method).

    By the way, have you considered talking to the CLR team? They need to make it possible to use T as void in generics. This would save you an enormous amount of duplication, and could be implemented as an empty struct plus optimizations (i.e. return a; -> return; if a is void, b = a -> no op if b and a are void, etc.). The Action/Func duality is entirely an artifact of this lack, and it's propagating into your API as well. Just a thought :)

  • Thanks for your thoughts on this, James.

  • As has been discussed previously, one of the new features in the Task Parallel Library is TaskCompletionSource&lt;TResult&gt;

  • What the documentation never states is that FromAsync will also throw whatever exception the BeginOperation method that is passed as an argument might throw. E.g. if you are passing as an argument a .Net APM operation that creates a network connection and the endpoint is not found you will get an exception right then and there when you call the FromAsync method and the task will not be created. This would merit a mention somewhere on msdn, but I couldn't find anything.

    I was trying to catch exceptions and return a faulted task (so that I only have to do error handling in one place) but that doesn't work:

               Task<Stream> requestTask = Task<Stream>.Factory.FromAsync(

                   (stream, callback, state) =>

                   {

                       try

                       {

                           return serviceProxy.BeginRequest(stream, callback, state);

                       }

                       catch (Exception ex)    // e.g. CommunicationException when endpoint can't be reached

                       {

                           TaskCompletionSource<Stream> tcs = new TaskCompletionSource<Stream>();

                           tcs.SetException(ex);

                           return tcs.Task;

                       }

                   },

                   (asyncResult) =>

                   {

                       return serviceProxy.EndRequest(asyncResult);

                   }, ...);

    The task returned in the case of an exception from BeginRequest is Waiting for Activation, not faulted. It looks like I have to wrap the call to FromAsync in a try/catch block. Any ideas?

  • Thanks for the doc suggestion, Fritz.

Page 1 of 1 (15 items)