Demo code source : Tasks6.zip

Introduction

This is the 6th and final part of my proposed series of articles on TPL. Last time I introduced Pipelines, and covered this ground:

  • BlockingCollection
  • BlockingCollection Basics
  • Simple Pipeline
  • More Complex Pipeline

This time we are going to be looking at some of the more advances TPL things you can do right now, which will conclude what we can do with TPL using the current .NET 4.0 classes available to us.

We will then proceed to look at what we will be able to do with C# 5 by using the new Async CTP

 

Article Series Roadmap

This is article 5 of a possible 6, which I hope people will like. Shown below is the rough outline of what I would like to cover.

  1. Starting Tasks / Trigger Operations / ExceptionHandling / Cancelling / UI Synchronization
  2. Continuations / Cancelling Chained Tasks
  3. Parallel For / Custom Partioner / Aggregate Operations
  4. Parallel LINQ
  5. Pipelines
  6. Advanced Scenarios / v.Next For Tasks (this article)

Table Of Contents

Pre-Requisites

As this article uses some new Community Technology Preview (CTP) bits that are not yet part of the .NET Framework you will need to download the Async CTP Refresh SP1 (which is what this article is based on), this can be downloaded right here:

http://www.microsoft.com/downloads/en/details.aspx?FamilyID=4738205d-5682-47bf-b62e-641f6441735b

 

Finishing Up TPL

This small section will hopefully finish up the few last remaining bits and peices that I still wanted to go through with TPL as it stands right now, you know with the classes you have available to you in .NET 4.0

AsyncFrom

Demo project name : AsyncFromBeginEnd/WCFService1

One of the neat things you can do with TPL is to use TPL with the older Asynchronous Programming Model (APM) by the use of TPLs inbuilt FromAsync which expects to create a Task based on the old familiar Begin/End methods that worked with a IAsyncResult interface. To demonstrate this I have constructed a rather simple WCF service (WCFService1 in the attached demo code), which I have then added a referennce to which also supports being called Asynchnously, as such there is the typical APM Begin/End IAsyncResult based methods that one would expect when working with APM.

Here is what the WCF service contract looks like.

[ServiceContract]
public interface IService1
{
    [OperationContract]
    List<String> GetData(int numberOf);
}

Which when added as a Service Reference with the async method support has proxy methods available as follows:

namespace AsyncFromBeginEnd.ServiceReference1 {
    
    
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    [System.ServiceModel.ServiceContractAttribute(ConfigurationName="ServiceReference1.IService1")]
    public interface IService1 {
        
        [System.ServiceModel.OperationContractAttribute(
            Action="http://tempuri.org/IService1/GetData", 
            ReplyAction="http://tempuri.org/IService1/GetDataResponse")]
        System.Collections.Generic.List<string> GetData(int numberOf);
        
        [System.ServiceModel.OperationContractAttribute(AsyncPattern=true, 
            Action="http://tempuri.org/IService1/GetData", 
            ReplyAction="http://tempuri.org/IService1/GetDataResponse")]
        System.IAsyncResult BeginGetData(int numberOf, System.AsyncCallback callback, object asyncState);
        
        System.Collections.Generic.List<string> EndGetData(System.IAsyncResult result);
    }
    
    ....
    ....
}

So how can we get a TPL Task from that older APM style code. Well it is actually very straight forward we simple do the following:

class Program
{
    static void Main(string[] args)
    {
        ServiceReference1.Service1Client client = 
		new ServiceReference1.Service1Client();

        Task<List<String>> task = 
            Task<List<String>>.Factory.FromAsync(
                client.BeginGetData(10, null, null),
                ar => client.EndGetData(ar));

        List<String> result = task.Result;
        Console.WriteLine("Successfully read all bytes using a Task");
        foreach (string s in result)
        {
            Console.WriteLine(s);
        }
        Console.ReadLine();
    }
}

And just to prove it all works here is the output

 

TaskCompletionSource

Demo project name : TaskCompletionSource1/TaskCompletionSource2

TaskCompletionSource<T> is a weird beast that could be used when you may need to produce a Task. In MSDNs own words

Represents the producer side of a System.Threading.Tasks.Task{TResult} unbound to a delegate, providing access to the consumer side through the System.Threading.Tasks.TaskCompletionSource<TResult>.Task property.

You can do all the normal things that you would expect a Task to do, such as have a result, Exception, Cancelled property, that you can set which would simulate what a Task would have done. I think the best way to see what this is all about is to see a couple of examples. So the  1st example is heavily borrowed directly from MSDN and looks like this:

using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;

namespace TaskCompletionSource1
{
    /// <summary>
    /// This example is adapted from Microsoft MSDN code freely available at
    /// http://msdn.microsoft.com/en-us/library/dd449174.aspx
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {

            TaskCompletionSource<String> tcs1 = 
		new TaskCompletionSource<String>();
            Task<String> task1 = tcs1.Task;

            // Complete tcs1 in background Task
            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000);
                tcs1.SetResult("Task1 Completed");
            });

            // Waits 1 second for the result of the task
            Stopwatch sw = Stopwatch.StartNew();
            String result = task1.Result;
            sw.Stop();

            Console.WriteLine("(ElapsedTime={0}): t1.Result={1} (expected \"Task1 Completed\") ", 
                sw.ElapsedMilliseconds, result);





            TaskCompletionSource<String> tcs2 = 
		new TaskCompletionSource<String>();
            Task<String> task2 = tcs2.Task;

            // Raise Exception tcs2 in background Task
            Task.Factory.StartNew(() =>
            {
                Thread.Sleep(1000);
                tcs2.SetException(new InvalidProgramException("Oh no...Something is wrong"));
            });

            sw = Stopwatch.StartNew();
            try
            {
                result = task2.Result;

                Console.WriteLine("t2.Result succeeded. THIS WAS NOT EXPECTED.");
            }
            catch (AggregateException e)
            {
                Console.Write("(ElapsedTime={0}): ", sw.ElapsedMilliseconds);
                Console.WriteLine(
			"The following exceptions have been thrown by t2.Result: (THIS WAS EXPECTED)");
                
		for (int j = 0; j < e.InnerExceptions.Count; j++)
                {
                    Console.WriteLine("\n-------------------------------------------------\n{0}", 
                        e.InnerExceptions[j].ToString());
                }
            }


            Console.ReadLine();

        }
    }
}

And has this output when run:

The 2nd example illustates a rather novel use of a TaskCompletionSource<T> where we use it to return a Task which has been delayed.

class Program
{
    static void Main(string[] args)
    {
        Stopwatch watch = new Stopwatch();
        watch.Start();
        Task<DateTimeOffset> delayTask = Delay(5000);
        Console.WriteLine(String.Format("Ellapsed Time 1 : {0}", watch.Elapsed));
        delayTask.ContinueWith((x) =>
            {
                Console.WriteLine(String.Format("Ellapsed Time 2 : {0}", watch.Elapsed));
            });
        Console.ReadLine();
    }



    public static Task<DateTimeOffset> Delay(int millisecondsTimeout)
    {
        var tcs = new TaskCompletionSource<DateTimeOffset>();
        new Timer(self =>
            {
                ((IDisposable)self).Dispose();
                tcs.TrySetResult(DateTime.Now);
            }).Change(millisecondsTimeout, - 1);
        return tcs.Task;
    }
}

And has this output when run:

 

That concludes what I wanted to say about TPL and what we have now in .NET 4 land, hope you have enjoyed the ride, next up we will spend some time looking into what things might be like for us in .NET 5 land.

 

Async CTP

Recently Microsoft released the Async CTP which is now in its 2nd CTP release, and a few things have changed (which I will not be mentioning in this article), this article will instead concentrate on what is allowable right now using the latest Async CTP (which was Async CTP Refresh SP1 at the time of writing this article).

One thing of particular note is that some of the code demonstrated in this section will use the CTP TaskEx classes which will eventually become part of the regular Task API. 

What Is The Async CTP All About

So what is so special about the Async CTP, we were quite happy with using Task(s) and the current TPL goodness no? Well to be honest yes, but all is not lost, far from it. The Async CTP simply builds apon Tasks/TPL in a very elegant manner. It does this by introducing 2 new key words and by supplying a whole bunch of new extension methods which transform the existing .NET classes into Awaitables (which is something we will cover very shortly).

The new key words are

Async : Which is something that must be used on methods that will have some asynchronous code running in them, that you wish to Await on.

Await : Simply allows us to wait for a Task (or custom Awaiter based object) result to be obtained.

This may not sound much, but what it does do for you is really cleans up your async code base to the point where it looks the same as a synchronous version would look. There is no changes to the program flow, no nasty call this callback for error, call this callback for success, not a IAsyncResult in sight. And to be honest to convert a synchronous method into a asynchronous method would be very very easily to do using the Async CTP.

We will see more of these key words as we progress, so lets carry on shall we. 

 

Our 1st Simple Async Example

Demo project name : SimpleAsync

Lets start with an insanely trivial example shall we, here is the code, not the highlighted Async/Await key words there, and also note that GetString() method actually returns a Task<String> even though the method body simple returns a String object. Neat huh.

Which outputs this when run:

So what exactly going on there, we have a Start() method that is marked up with the new Async key word, which tells the compiler that this method will be run asynchronously, and then we have a String which is returns via the GetString() method, that really returns a Task<String> that we are awaiting on using the new Await key word, when we are really returning a String in the GetString() method body. Huh?

Well to break it down a bit, the compiler does some work for you (which you will see in a minute) that know what to emit when it sees an Async key word being used. The next peice to the puzzle is that Task has been vamped up to become a Awaitable object (more on this later), and thanks also to the CTP is that we can return a Task<String> by simply returning a String in the GetString() method body.

What the compiler does is treat the code after the Await as a continuation that is run after the work the Await keyword has run to completion.

If we look at what we get in Reflector for this trivial example, we can see that it is converted into some sort of state machine type code.

internal class Program
{
    // Methods
    private Task<string> GetString()
    {
        <GetString>d__5 d__ = new <GetString>d__5(0);
        d__.<>4__this = this;
        d__.<>t__MoveNextDelegate = new Action(d__, (IntPtr) this.MoveNext);
        d__.$builder = AsyncTaskMethodBuilder<string>.Create();
        d__.MoveNext();
        return d__.$builder.Task;
    }

    private static void Main(string[] args)
    {
        new Program().Start();
    }

    private void Start()
    {
        <Start>d__0 d__ = new <Start>d__0(0);
        d__.<>4__this = this;
        d__.<>t__MoveNextDelegate = new Action(d__, (IntPtr) this.MoveNext);
        d__.$builder = AsyncVoidMethodBuilder.Create();
        d__.MoveNext();
    }

    // Nested Types
    [CompilerGenerated]
    private sealed class <GetString>d__5
    {
        // Fields
        private bool $__disposing;
        public AsyncTaskMethodBuilder<string> $builder;
        private int <>1__state;
        public Program <>4__this;
        public Action <>t__MoveNextDelegate;
        public StringBuilder <sb>5__6;

        // Methods
        [DebuggerHidden]
        public <GetString>d__5(int <>1__state)
        {
            this.<>1__state = <>1__state;
        }

        [DebuggerHidden]
        public void Dispose()
        {
            this.$__disposing = true;
            this.MoveNext();
            this.<>1__state = -1;
        }

        public void MoveNext()
        {
            string <>t__result;
            try
            {
                if (this.<>1__state == -1)
                {
                    return;
                }
                this.<sb>5__6 = new StringBuilder();
                this.<sb>5__6.AppendLine("Hello world");
                <>t__result = this.<sb>5__6.ToString();
            }
            catch (Exception <>t__ex)
            {
                this.<>1__state = -1;
                this.$builder.SetException(<>t__ex);
                return;
            }
            this.<>1__state = -1;
            this.$builder.SetResult(<>t__result);
        }
    }

    [CompilerGenerated]
    private sealed class <Start>d__0
    {
        // Fields
        private bool $__disposing;
        public AsyncVoidMethodBuilder $builder;
        private int <>1__state;
        public Program <>4__this;
        public Action <>t__MoveNextDelegate;
        private TaskAwaiter<string> <a1>t__$await3;
        public string <chungles>5__1;

        // Methods
        [DebuggerHidden]
        public <Start>d__0(int <>1__state)
        {
            this.<>1__state = <>1__state;
        }

        [DebuggerHidden]
        public void Dispose()
        {
            this.$__disposing = true;
            this.MoveNext();
            this.<>1__state = -1;
        }

        public void MoveNext()
        {
            try
            {
                string <1>t__$await2;
                bool $__doFinallyBodies = true;
                if (this.<>1__state != 1)
                {
                    if (this.<>1__state != -1)
                    {
                        Console.WriteLine("*** BEFORE CALL ***");
                        this.<a1>t__$await3 = this.<>4__this.GetString().GetAwaiter<string>();
                        if (this.<a1>t__$await3.IsCompleted)
                        {
                            goto Label_0084;
                        }
                        this.<>1__state = 1;
                        $__doFinallyBodies = false;
                        this.<a1>t__$await3.OnCompleted(this.<>t__MoveNextDelegate);
                    }
                    return;
                }
                this.<>1__state = 0;
            Label_0084:
                <1>t__$await2 = this.<a1>t__$await3.GetResult();
                this.<a1>t__$await3 = new TaskAwaiter<string>();
                this.<chungles>5__1 = <1>t__$await2;
                Console.WriteLine("*** AFTER CALL ***");
                Console.WriteLine("result = " + this.<chungles>5__1);
                Console.ReadLine();
            }
            catch (Exception <>t__ex)
            {
                this.<>1__state = -1;
                this.$builder.SetException(<>t__ex);
                return;
            }
            this.<>1__state = -1;
            this.$builder.SetResult();
        }
    }
}

What we can also see in there are things like GetAwaiter which is a pattern based thing that makes objects awaitable. We will see what the GetAwaiter does, and how we can make our own objects awaitable shortly, for now you should be just about to be able to make out that there is some compiler generated code, and there is a continuation delegate (<>t__MoveNextDelegate in the reflected code above) that gets called in that reflected code.

One thing that I remember seeing in the Anders Mix Async CTP video was that the Async CTP offers affords responsiveness, while we are awaiting, control is relinquished back to the calling thread. 

Catching Exceptions

Demo project name : TryCatchAwaitTask

Catching Exceptions in the Async CTP could not be easier and like I say strongly follows the control flow one would use when running synchronous code, no code smell at all, simple try/catch stuff all the way, here is a small example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TryCatchAwaitTask
{
    class Program
    {
        static void Main(string[] args)
        {
            Program p = new Program();
            p.DoIt();
        }


        public async void DoIt()
        {
            //No problems with this chap
            try
            {
                List<int> results = await 
                    GetSomeNumbers(10,20);
                Console.WriteLine("==========START OF GOOD CASE=========");
                Parallel.For(0, results.Count, (x) =>
                {
                    Console.WriteLine(x);
                });
                Console.WriteLine("==========END OF GOOD CASE=========");

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }


            //Make something go wrong
            try
            {
                //simulate a failure by erroring at 5
                List<int> results = await GetSomeNumbers(10,5);
                Parallel.ForEach(results, (x) =>
                {
                    Console.WriteLine(x);
                });

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            Console.ReadLine();

        }


        /// <summary>
        /// Throws InvalidOperationException when index > shouldFailAt value
        /// </summary>
        public async Task<List<int>> GetSomeNumbers(int upperLimit, int shouldFailAt)
        {
            
            List<int> ints = new List<int>();
            for (int i = 0; i < upperLimit; i++)
            {
                if (i > shouldFailAt)
                    throw new InvalidOperationException(
                        String.Format("Oh no its > {0}",shouldFailAt));

                ints.Add(i);
            }
            return ints;
            
                
        }
    }
}

And here is what is shown when this is run:

 

UI Responsiveness

Demo project name : UIResponsiveness

Another area where the Async CTP is very useful is within any area where you still need responsiveness (such as a UI), the following code, simply demonstrates that the user may spawn multiple awaitable bits of code, which are all running but the UI remains responsive, simply open the demo and click the buttons randomly to see what I mean.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;

namespace UIResponsiveness
{
    public partial class Form1 : Form
    {
        Random rand = new Random();
        List<string> suffixes = new List<string>() { 
            "a","b","c","d","e","f","g","h","i","j","k","l",
            "m","n","o","p","q","r","s","t","u","v","w","x","y","z"};


        public Form1()
        {
            InitializeComponent();


        }

        private async void button1_Click(object sender, EventArgs e)
        {
            RunTaskToGetText();
        }

        private void button2_Click(object sender, EventArgs e)
        {
            RunTaskToGetText();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            RunTaskToGetText();
        }


        private async void RunTaskToGetText()
        {
            List<string> results = await GetSomeText(
                suffixes[rand.Next(0,suffixes.Count())], 500000);

            textBox1.Text += results[0];
            textBox1.Text += results[1];
            textBox1.Text += results[results.Count-2];
            textBox1.Text += results[results.Count-1];
        }



        public async Task<List<string>> GetSomeText(string prefix, int upperLimit)
        {
            List<string> values = new List<string>();
            values.Add("=============== New Task kicking off=================\r\n");
            for (int i = 0; i < upperLimit; i++)
            {
                values.Add(String.Format("Value_{0}_{1}\r\n", prefix, i.ToString()));
            }
            values.Add("=============== New Task Done=================\r\n");
            return values;
        }
    }
}

I can not really show a demo screen shot for this one, but if you try the demo code, you will see it remains reasponsive and results come back when they are finished, and no longer need to be awaited on

 

Supporting Progress

Demo project name : ProgressReporting

The Async CTP also deals with reporting of progress, which is something that was not that easy to do in TPL. So how does it do this, well there is a new interface in the Async CTP called IProgress<T> which has been implemented for you in the CTP by the Progress<T> class. Where these look like this:

public interface IProgress<in T>
{
    void Report(T value);
}





public class Progress<T> : IProgress<T>
{
    public Progress();

    public Progress(Action<T> handler);

    public event ProgressEventHandler<T> ProgressChanged;

    protected virtual void OnReport(T value);
}

So how do we use the Progress<T> in our own code, well its pretty simple, here is a simple example, which simply writes the current progress value out to the Console. I should point out that working out the correct progress value is up to you to come up with

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ProgressReporting
{
    class Program
    {
        static void Main(string[] args)
        {
            Program p = new Program();
            p.DoIt();
        }


        public async void DoIt()
        {
            Progress<int> progress = new Progress<int>();
            progress.ProgressChanged += (sender, e) =>
            {
                Console.WriteLine(String.Format("Progress has seen {0} item", e));
            };

            List<int> results = await GetSomeNumbers(10, progress);
            Console.WriteLine("Task results are");
            Parallel.For(0, results.Count, (x) =>
            {
                Console.WriteLine(x);
            });

            Console.ReadLine();
        }



        public async Task<List<int>> GetSomeNumbers(
		int upperLimit, IProgress<int> progress)
        {
            List<int> ints = new List<int>();
            for (int i = 0; i < upperLimit; i++)
            {
                ints.Add(i);
                progress.Report(i + 1);
            }
            return ints;
        }

    }
}

And here is an example of it running:

 

 

Awaiter/GetAwaiter

So now that we have seen some examples of the new Async CTP key words in action, lets take a deeper look at just what it means to be awaitable.

The Await key word CAN ONLY be used with something that is awaiatble. Thanks to the Async CTP Task has been extended to be awaitable. So how does it do that exactly, and can we make our own types awaitable.

Well yes we can, as it turns out all you need to do is ensure that certain core methods that are expected are present, which can be instance based or extension methods.

The things you must implement to make your own objects awaiatble or extend an existing object are as follows:

public void GetResult()
public void OnCompleted(Action continuation)
public bool IsCompleted { get; set; }

Which will be expected when the compiler generated code that is created in response to using the new Await keyword is used, which internally simply calls the GetAwaiter() method in the compiler re-written code, so as long as your object has these methods/property you are awaitable.

 

Awaiter With Return Value

Demo project name : AwaiterThatReturnsSomething

So now that we know how to make a custom awaitable object lets try it out, by doing something trivial like adding the ability to wait for a double to be raised to the power of something you specify (which is obviously no use for anything other than a demo, but you get the idea from it I would hope).

So we start with creating an extension method on Double as follows, which as you can see returns a DoubleAwaiter

public static class DoubleExtensionMethods
{
    public static DoubleAwaiter GetAwaiter(this double demoDouble, int power)
    {
        return new DoubleAwaiter(demoDouble, power);
    }
}

Where the DoubleAwaiter looks like this, where the most important parts are that we set the IsCompleted we call the continuation Action

public class DoubleAwaiter
{
    private double theValue;
    private int power;

    public DoubleAwaiter(double theValue, int power)
    {
        this.theValue = theValue;
        this.power = power;
        IsCompleted = false;
    }

    public DoubleAwaiter GetAwaiter()
    {
        return this;
    }

    public double GetResult()
    {
        return theValue;
    }


    public void OnCompleted(Action continuation)
    {
        this.theValue = Math.Pow(theValue, power);
        IsCompleted = true;
        continuation();
    }

    public bool IsCompleted { get; set; }
}

Which we can now use like this

class Program
{
    static void Main(string[] args)
    {
        ThreadPool.QueueUserWorkItem(async delegate
        {
            double x = 10;
            double x2 = await x.GetAwaiter(3);
            Console.WriteLine(string.Format("x was : {0}, x2 is {1}",x,x2));
        });

        Console.ReadLine();
    }
}

Which when run produces the following output (like I say this is about the dumbest use of a custom awaitable you could imagine it is purely there to show you the structure you need to need to use if you want to make you own code awaitable)

 

Synchronization Awaiter

Demo project name : SyncContextControlAwaiter

Since the concept of making something awaitable really boils down to implementing the correct couple of methods/properties we can exploit this to do some pretty wild stuff, for example you must have come up against the need to invoke a cross thread call back to the UI thread when dealing with UI code.

Well we can actually use a custom awaiter that will make this whole process a lot neater, consider these portions of Windows Forms code:

public static class ControlExtensionMethods
{
    public static ControlAwaiter GetAwaiter(this Control control)
    {
        return new ControlAwaiter(control);
    }
}



public class ControlAwaiter
{
    private readonly Control control;

    public ControlAwaiter(Control control)
    {
        if (control == null) 
	      throw new ArgumentNullException("control");
        this.control = control;
        IsCompleted = false;
    }


    public void GetResult()
    {
    }


    public void OnCompleted(Action continuation)
    {
        control.BeginInvoke(continuation);
        IsCompleted = true;
    }

    public bool IsCompleted { get; set; }
}

Where we are using the custom awaiter to do the marshalling back to the UI thread for the Windows Forms app using BeginInvoke(..), where we had this calling code:

private void BtnSyncContextAwaiter_Click(object sender, EventArgs e)
{
    ThreadPool.QueueUserWorkItem(async delegate
    {
        string text = "This should work just fine thanks to our lovely \"ControlAwaiter\" " +
                        "which ensures correct thread marshalling";
        await textBox1;
        textBox1.Text = text;
    });

}

The demo demonstrates using the custom synchronization context awaiter shown above.

The demo code also demonstrates what the code would do when we don't use the custom synchronization context awaiter shown above, which uses this code

private void BtnNoAwaiter_Click(object sender, EventArgs e)
{
    ThreadPool.QueueUserWorkItem(delegate
    {
        try
        {
            string text = "This should cause a problem, as we have spawned " +
                            "background thread using ThreadPool" + 
                            "Which is not the correct thread to change the UI control " +
                            "so should cause a CrossThread Violation";
            textBox1.Text = text;
        }
        catch (InvalidOperationException ioex)
        {
            MessageBox.Show(ioex.Message);
        }
    });
}

Here is a screen shot of the demo code using the custom synchronization context awaiter shown above

And here is a screen shot of the demo code NOT using the custom synchronization context awaiter shown above

 

Realistic Awaiter

Demo project name : MoreRealisticAwaiterWithCancellation

Now that you have seen that you can create your own awaitable code and even create some pretty novel uses of it, I think you should know that most of the time you will be using Task(s) as what you Await on. So this last example illustrates a fuller example that uses Task(s) and also shows you how you might go about Unit testing some Task centric service code that you are awaiting on, that is constructed in a manner that we could easily inject an alternative service other than Task centric service code.

This demo also demonstrates how to use a CancellationToken to cancel a awaitable Task.

So lets start with the actual code that does the awaiting, which looks like this:

class Program
{
    static void Main(string[] args)
    {
        ManualResetEvent mre1 = new ManualResetEvent(true);
        ManualResetEvent mre2 = new ManualResetEvent(false);
        ManualResetEvent mre3 = new ManualResetEvent(false);
        ManualResetEvent mre4 = new ManualResetEvent(false);

        DoItForReal(false, mre1, mre2);
        DoItForReal(true, mre2, mre3);

        Console.ReadLine();
    }


    /// <summary>
    /// Shows how you can await on a Task based service
    /// </summary>
    private static async void DoItForReal(
        bool shouldCancel, ManualResetEvent mreIn, ManualResetEvent mreOut)
    {

        mreIn.WaitOne();

        CancellationTokenSource cts = new CancellationTokenSource();

        int upperLimit = 50;
        int cancelAfter = (int)upperLimit / 5;
        // which is what is used in WorkProvider class
        int waitDelayForEachDataItem = 10; 

        //allows some items to be processed before cancelling
        if (shouldCancel)
            cts.CancelAfter(cancelAfter * waitDelayForEachDataItem); 

        Console.WriteLine();
        Console.WriteLine("=========================================");
        Console.WriteLine("Started DoItForReal()");


        try
        {
            List<String> data = await new Worker(new WorkProvider(), 
                upperLimit, cts.Token).GetData();

            foreach (String item in data)
            {
                Console.WriteLine(item);
            }
            //allow those waiting on this WaitHandle to continue
            if (mreOut != null) { mreOut.Set(); } 
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Processing canceled.");
            //allow those waiting on this WaitHandle to continue
            if (mreOut != null) { mreOut.Set(); }
        }
        catch (AggregateException aggEx)
        {
            Console.WriteLine("AggEx caught");
            //allow those waiting on this WaitHandle to continue
            if (mreOut != null) { mreOut.Set(); }
        }
        finally
        {
            Console.WriteLine("Finished DoItForReal()");
            Console.WriteLine("=========================================");
            Console.WriteLine();
        }
    }


}

The use of the ManualResetEvent(s) are there just to ensure that one scenario finishes printing to the console before the other scenario starts, just to keep the demo print out screen shot clear for you readers

So nows lets examine the Worker code that the code above Awaits on

public class Worker
{
    private IWorkProvider workprovider;
    private int upperLimit;
    private CancellationToken token;

    public Worker(IWorkProvider workprovider, int upperLimit, CancellationToken token)
    {
        this.workprovider = workprovider;
        this.upperLimit = upperLimit;
        this.token = token;
    }

    public Task<List<String>> GetData()
    {
        return workprovider.GetData(upperLimit, token);
    }
}

It can be seen that design supports an alternative IWorkProvider being supplied either from a Unit test or maybe by the use of an abstract factory that is being used in conjunction with a IOC container to resolve the actual Worker instance.

The IWorkProvider  interface in the demo app looks like this:

public interface IWorkProvider
{
    Task<List<String>> GetData(int upperLimit, CancellationToken token);
}
 

The actual code that is being awaited on is the Task<List<String>> that is returned from the GetData() method. Lets now have a look at the actual Task based service code that provides the Task<List<String>>

public class WorkProvider : IWorkProvider
{
    public Task<List<string>> GetData(int upperLimit, System.Threading.CancellationToken token)
    {
	//will not be TaskEx when CTP is in .NET 5.0 Framework
        return TaskEx.Run(() =>
        {
            List<string> results = new List<string>();

            for (int i = 0; i < upperLimit; i++)
            {
                token.ThrowIfCancellationRequested();
                Thread.Sleep(10);  
                results.Add(string.Format("Added runtime string {0}",i.ToString()));
            }
            return results;
        });
    }
}

Now when we run the actual code that uses this Task based service code we get the following, where we run it once to completion, and once where we expect it to be cancelled as the demo code initiated a cancel via a CancellationTokenSource

Now you may be asking yourself, well that's great I have some Task based service code, which we can Await on, but how the heck am I supposed to be able to Unit test that bad boy. Well although the demo app does not include a formal Unit test, it provides all the peices to the puzzle, such as

  1. Seperation of concern
  2. Extension point for IOC to supply an alternative IWorkProvider implementation, or for a Unit test to do so.
  3. Mocking of the IWorkProvider which shows you how you can mock your Task(s)

I prefer to use Moq (cool mocking framework) to carry out any test code that I do, so here is an example of how you might write a Unit test based mocked service that will act the same as the Task based service code, we just saw

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Moq;
using System.Threading.Tasks;

namespace MoreRealisticAwaiterWithCancellation
{
    class Program
    {
        static void Main(string[] args)
        {
		
	    ...
	    ...
	    ...

            DoItUsingMoq_YouKnowForUnitTestsLike(mre3);
            
            
            Console.ReadLine();
        }




        /// <summary>
        /// Shows how you might mock a task based service using "Moq" and TaskCompletionSource
        /// </summary>
        private static async void DoItUsingMoq_YouKnowForUnitTestsLike(ManualResetEvent mreIn)
        {

            mreIn.WaitOne();
            CancellationTokenSource cts = new CancellationTokenSource();

            int upperLimit = 50;
            
            List<String> dummyResults = new List<string>();
            for (int i = 0; i < upperLimit; i++)
		    {
                dummyResults.Add(String.Format("Dummy Result {0}", i.ToString()));     
		    }
            
            //Allows this test method to simulate a Task with a result without actually creating a Task
            TaskCompletionSource<List<String>> tcs = 
                new TaskCompletionSource<List<String>>();
            tcs.SetResult(dummyResults);


            Console.WriteLine();
            Console.WriteLine("=========================================");
            Console.WriteLine("Started DoItUsingMoq_YouKnowForUnitTestsLike()");

            try
            {
                Mock<IWorkProvider> mockWorkProvider = new Mock<IWorkProvider>();
                mockWorkProvider
                    .Setup(x => x.GetData(
                        It.IsAny<Int32>(), 
                        It.IsAny<CancellationToken>()))
                    .Returns(tcs.Task);

                List<String> data = await new Worker(
                    mockWorkProvider.Object, upperLimit, cts.Token).GetData();

                foreach (String item in data)
                {
                    Console.WriteLine(item);
                }
            }
            finally
            {
                Console.WriteLine("Finished DoItUsingMoq_YouKnowForUnitTestsLike()");
                Console.WriteLine("=========================================");
                Console.WriteLine();
            }
        }
    }
}

It can be seen that this example uses Moq to create  a mock IWorkProvider which is supplied to the Worker constructor which is what we will end up waiting on. So if we are in a Unit test and not actually running a Task based service, how are we ever going to run the awaiting continuation. Well the answer lies at the beginning of this article, we simply use a TaskCompletionSource to simulate a Task.Result by using the TaskCompletionSource.SetResult(..) method and supplying the TaskCompletionSource.Task to the mock object that will be used in this test code.

As always here is an example of this running.

I did not do anything with the CancellationToken here, but this could be accomplished if you really wanted to I feel. Moq is very cool and would be more than able to do the job I think, I was just tired by this point, sorry.

 

That's It For Now

That is all I wanted to say in this in this article. I hope you liked it, and have enjoyed the TPL ride, I know it has been a long ride, and too be honest there have been times when I thought I would never finish it off, it is finally done. So I ask if you did like this article, could you please spare some time to leave a comment and a vote. Many thanks.

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"