Click here to Skip to main content
16,017,881 members
Articles / General Programming / Threads

Task Parallel Library: 6 of n

Rate me:
Please Sign up or sign in to vote.
4.95/5 (71 votes)
10 May 2011CPOL14 min read 171.9K   2.1K   152   53
A look into using the Task Parallel Library.

Introduction

This is the sixth 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 advanced 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 6 of 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 Partitioner / Aggregate Operations
  4. Parallel LINQ
  5. Pipelines
  6. Advanced Scenarios / v.Next for Tasks (this article)

Table of Contents

Prerequisites

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:

Finishing up TPL

This small section will hopefully finish up the few last remaining bits and pieces that I still wanted to go through with TPL as it stands right now; you know with the classes you have available 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 TPL's inbuilt FromAsync which expects to create a Task based on the old familiar Begin/End methods that worked with the 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 reference to, which also supports being called asynchronously; 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:

C#
[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:

C#
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 straightforward; we simply do the following:

C#
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:

Image 1

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 MSDN's 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 first example is heavily borrowed directly from MSDN, and looks like this:

C#
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:

Image 2

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

C#
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:

Image 3

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 second CTP release, and a few things have changed (which I will not be mentioning in this article). This article will concentrate on what is allowable right now using the latest Async CTP (which is 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, right? Well, to be honest, yes, but all is not lost, far from it. The Async CTP simply builds upon Tasks/TPL in a very elegant manner. It does this by introducing two new keywords 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 keywords 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 change to the program flow, no nasty call this callback for error, call this callback for success, not an IAsyncResult in sight. And to be honest, to convert a synchronous method into an asynchronous method would be very, very easy to do using Async CTP.

We will see more of these key ords as we progress, so let's carry on, shall we?

Our First Simple Async Example

Demo project name: SimpleAsync

Let's start with an insanely trivial example, shall we? Here is the code. Note the highlighted Async/Await keywords there, and also note that the GetString() method actually returns a Task<String> even though the method body simple returns a String object. Neat, huh?

Image 4

Which outputs this when run:

Image 5

So what exactly is going on here? We have a Start() method that is marked up with the new Async keyword, which tells the compiler that this method will be run asynchronously, and then we have a String which is returned via the GetString() method that really returns a Task<String> that we are awaiting on using the new Await keyword, 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 knows what to emit when it sees an Async keyword being used. The next piece to the puzzle is that Task has been vamped up to become an Awaitable object (more on this later), and thanks also to the CTP, 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 of 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.

C#
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 just about 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 Async CTP offers responsiveness; while we are awaiting, the control is relinquished back to the calling thread.

Catching Exceptions

Demo project name: TryCatchAwaitTask

Catching exceptions in 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:

C#
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:

Image 6

UI Responsiveness

Demo project name: UIResponsiveness

Another area where 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.

C#
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 screenshot for this one, but if you try the demo code, you will see it remains responsive 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 Async CTP called IProgress<T> which has been implemented for you in the CTP by the Progress<T> class. These look like this:

C#
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 Progress<T> in our own code? Well, it's pretty simple. Here is a simple example which 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:

C#
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:

Image 7

Awaiter/GetAwaiter

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

The Await keyword can only be used with something that is awaiatble. Thanks to 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:

C#
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, let's 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.

C#
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 and we call the continuation Action.

C#
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:

C#
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 use if you want to make you own code awaitable).

Image 8

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 callback 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:

C#
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:

C#
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:

C#
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 screenshot of the demo code using the custom synchronization context awaiter shown above:

Image 9

And here is a screenshot of the demo code not using the custom synchronization context awaiter shown above:

Image 10

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 an awaitable Task.

Let's start with the actual code that does the awaiting, which looks like this:

C#
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 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 screenshot clear for you readers.

Now let's examine the Worker code that the code above Awaits on:

C#
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 the 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 an IOC container to resolve the actual Worker instance.

The IWorkProvider interface in the demo app looks like this:

C#
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. Let's now have a look at the actual Task based service code that provides the Task<List<String>>.

C#
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.

Image 11

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 pieces 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 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 that we just saw:

C#
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:

Image 12

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 article. I hope you liked it and have enjoyed the TPL ride. I know it has been a long ride, and to 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
GeneralMy vote of 5 Pin
torrentgr31-Jan-13 1:38
torrentgr31-Jan-13 1:38 
GeneralMy vote of 5 Pin
Simon_Whale22-Dec-11 3:49
Simon_Whale22-Dec-11 3:49 
GeneralMy vote of 5 Pin
Saurabh Singh _4-Jul-11 23:30
Saurabh Singh _4-Jul-11 23:30 
GeneralMy vote of 5 Pin
Monjurul Habib17-Jun-11 10:39
professionalMonjurul Habib17-Jun-11 10:39 
GeneralMy vote of 5 Pin
Rhuros15-Jun-11 2:24
professionalRhuros15-Jun-11 2:24 
GeneralMy vote of 5 Pin
Abhinav S14-Jun-11 0:32
Abhinav S14-Jun-11 0:32 
GeneralWCF call fails Pin
rkb3-Jun-11 11:17
rkb3-Jun-11 11:17 
GeneralRe: WCF call fails Pin
Sacha Barber5-Jun-11 19:59
Sacha Barber5-Jun-11 19:59 
GeneralRe: WCF call fails Pin
rkb6-Jun-11 5:08
rkb6-Jun-11 5:08 
GeneralRe: WCF call fails Pin
Sacha Barber6-Jun-11 6:28
Sacha Barber6-Jun-11 6:28 
GeneralRe: WCF call fails Pin
Sacha Barber6-Jun-11 6:29
Sacha Barber6-Jun-11 6:29 
GeneralMy vote of 5 Pin
Iftikhar Akram30-May-11 0:50
Iftikhar Akram30-May-11 0:50 
GeneralMy vote of 5 Pin
tbayart29-May-11 23:47
professionaltbayart29-May-11 23:47 
GeneralMy vote of 5 Pin
Wendelius21-May-11 8:10
mentorWendelius21-May-11 8:10 
GeneralRe: My vote of 5 Pin
Sacha Barber22-May-11 20:09
Sacha Barber22-May-11 20:09 
GeneralMy vote of 5! Pin
Filip D'haene19-May-11 9:55
Filip D'haene19-May-11 9:55 
GeneralRe: My vote of 5! Pin
Sacha Barber19-May-11 10:04
Sacha Barber19-May-11 10:04 
GeneralGreat article Pin
BillW3318-May-11 5:36
professionalBillW3318-May-11 5:36 
GeneralRe: Great article Pin
Sacha Barber18-May-11 5:55
Sacha Barber18-May-11 5:55 
GeneralMy vote of 5 Pin
Slacker00718-May-11 0:24
professionalSlacker00718-May-11 0:24 
GeneralRe: My vote of 5 Pin
Sacha Barber18-May-11 1:31
Sacha Barber18-May-11 1:31 
GeneralMy vote of 5 Pin
alain_dionne17-May-11 2:52
professionalalain_dionne17-May-11 2:52 
GeneralRe: My vote of 5 Pin
Sacha Barber17-May-11 3:30
Sacha Barber17-May-11 3:30 
GeneralMy vote of 5 Pin
hoernchenmeister16-May-11 22:16
hoernchenmeister16-May-11 22:16 
GeneralRe: My vote of 5 Pin
Sacha Barber17-May-11 3:31
Sacha Barber17-May-11 3:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.