Using .NET 4.0 Tasks with the AsyncEnumerator

4 Comments October 12, 2009

I’ve been doing a lot of work with the new Task class that ships with .NET 4.0 as I’ve been revising my CLR via C# book (due out in early 2010).

Task are really good for performing asynchronous compute-bound work and while my AsyncEnumerator was really designed for performing I/O-bound work using the CLR’s APM, it is possible to use Tasks with the AsyncEnumerator giving you the ability to easily perform I/O-bound as well as compute-bound work and use the AsyncEnumerator to coordinate it all.

 

For example, you might want to read a bitmap image into memory from disk or network asynchronously (this is I/O bound) and then use Tasks to produce one or more thumbnail images (this is compute-bound) and then write the thumbnail images back to disk/network (I/O-bound again).

To use Tasks within an AsyncEnumerator, just create the Task(s) as you normally would and add a ContinueWith task that simply calls AsyncEnumerator’s End method to get a delegate. Then, invoke this delegate passing the task itself (since Tasks implement the IAsyncResult interface). Or, if you don’t need to identify the task, you can simplify the code some and just pass null. On the other side of the yield return statement, you need to call AsyncEnumerator’s DequeueAsyncResult to remove the entry from the AsyncEnumerator’s IAsyncResult collection but then there is no EndXxx method to call.

 

Here is a simple example, that starts a Task that sleeps for 10 seconds and then returns the current DateTime.

 

private static IEnumerator<Int32> AsyncEnumeratorAndTasks(AsyncEnumerator ae) {

   var t = new Task<DateTime>(() => { Thread.Sleep(10000); return DateTime.Now; });

   t.Start();

 

   // The Task tells the AsyncEnumerator when it is done

   // If you don’t need to identify the Task, you can pass ‘null’ instead of ‘task’

   t.ContinueWith(task => ae.End()(task));

 

   yield return 1;      // Waits for the 1 Task to complete

 

   // You MUST call DequeueAsyncResult to Remove the entry form the AsyncEnumerator object’s collection

   // Casting the return value and assigning to ‘t’ is not necessary; since ‘t’ already refer to the same Task object

   t = (Task<DateTime>) ae.DequeueAsyncResult();        

 

   Console.WriteLine(t.Result);     // Shows the DateTime when the Task completed
}

 

-- Jeffrey Richter (http://Wintellect.com)

 


4 Comments

  • Gravatar Image
    Tuomas Hietanen October 12, 2009 11:06 AM

    Hmm... So now we have normal threads, these tasks and soon the Reactive Framework way...

  • Gravatar Image
    progg.ru October 12, 2009 12:31 PM

    Thank you for submitting this cool story - Trackback from progg.ru

  • Gravatar Image
    Andrew Borodin October 2, 2010 6:40 AM

    What if instead of IEnumerator we would return IEnumerable>?
    We could say what exactly are we waiting for (:

  • Gravatar Image
    cornel February 21, 2013 3:21 AM

    Why can't we simply have t.ContinueWith(_ => Console.WriteLine(_.Result)) in the first place?
    For IO bound operations one can use the Task.Factory.FromAsync extensions to get a suitable Task.

Have a Comment?

Archives