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; });



   // 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 (https://training.atmosera.com)