C# 5.0 : playing with Async CTP

Introduction

After seeing the TechEd presentation about the future of C#, I couldn’t resist to play with the new C# stuff! So I turn my VMWare on and install the CTP on my test VM (From what I read in forum you can uninstall it clean, but better take some elementary precautions with early release stuff).

The goal of the CTP is to show how it will be easy to make asynchronous call with C# 5.0. In order to do that, they introduced two new keywords :

Sync and Await

First of all, you need to add a reference to AsyncCtpLibrary.dll. The library should be located in DocumentsMicrosoft Visual Studio Async CTPSamples

An example is better than 1000 words : Let’s make a (trivial) console application that will call a function that takes time.

Before :

 

class Program
    {
        static void Main(string[] args)
        {
            var p = new Program();
            p.Run();
            Console.WriteLine("Press a key to quit.");
            Console.ReadKey();
        }

        public void Run()
        {
            Console.WriteLine("Starting long function.");
            LongFunction();
            Console.WriteLine("Long function finished.");
        }

        private void LongFunction()
        {
            for (int i = 0; i < 10000; i++)
            {
                Thread.SpinWait (10000000);

            }
        }

    }

After:

class Program
{
    static void Main(string[] args)
    {
        var p = new Program();
        p.RunAsync();
        Console.WriteLine("Press a key to quit.");
        Console.ReadKey();
    }


    public async void RunAsync()
    {
        Console.WriteLine("Starting long function.");
        await Task.Factory.StartNew(() => LongFunction());
        Console.WriteLine("Long function finished.");
    }

    private void LongFunction()
    {
        for (int i = 0; i < 10000; i++)
        {
           Thread.SpinWait (10000000);
        }
    }

}

As you see now, you arrive to line 7 immediately. The UI is responsive, even though the LongFunction is not finished. Sounds like F# let! or do! keywords.

The changed lines are highlighted. Here are the details:

  • Line 11: The Run function is now called RunAsync. It is not mandatory, but it is a handy convention to put an Async prefix for asynchronous function name. Async keyword in function declaration tells the compiler the function is asynchronous and thus make some transformation to implement it asynchronously. Async functions should return void, Task or Task. Note that you can only use the await keyword in the function body if that function is declared with async.
  • Line 14: The await keywords here tells the compiler this call is asynchronous. So, the application flow will switch to the caller function while the  await expression is not done. When the expression is done,it will resume on next line the await . Note that await doesn't accept void expression (Check Calling the (a)waiter part of this post to know why), so I created a Task.

And here we are : asynchronous function!

Now, let's play a little bit more...

Cancellation

If, for whatever reason you should terminate a task before it is done, it is always a good thing to warn it, so it can stop. Otherwise, the Task will continue running on another thread until completion, which is, at least,a waste of CPU resource.

So, how do we do it? Well, as the Async framework uses .NET 4.0 new TPL (Task Parallel Library), we use TPL's CancellationToken.

Here is the big picture :

First, create a CancellationTokenSource. The class creates a CancellationToken. We will pass the token to the async function. In the loop of the long running function, we will check the status of the token. If it is cancelled, then we stop processing the task.

Example :

class Program
{
    private static CancellationTokenSource _cts;
    static void Main(string[] args)
    {
        // Create a CancellationTokenSource
        _cts = new CancellationTokenSource();

        var p = new Program();

        // Pass the token to the function
        p.RunAsync(_cts.Token);
        Console.WriteLine("Press a key to cancel.");
        Console.ReadKey();
        _cts.Cancel();
        Console.WriteLine("Press a key to quit.");
        Console.ReadKey();
    }

    public async void RunAsync(CancellationToken token)
    {
        Console.WriteLine("Starting long function.");
        // Pass the token to the function
        await Task.Factory.StartNew(() => LongFunction(token));
        // Check if task was cancelled
        if (token.IsCancellationRequested)
            Console.WriteLine("Long function cancelled.");
        else
            Console.WriteLine("Long function finished.");
    }

    private void LongFunction(CancellationToken token)
    {
        for (int i = 0; i < 10000; i++)
        {
            Thread.SpinWait (10000000);
            // Check if cancellation requested. If so, stop processing
            if (token.IsCancellationRequested)
                return;
        }
    }
}

Pretty simple, huh? But effective, if you check the task manager, you will see the CPU usage drop, proving the task stopped.

Now, let's make some progress!

Progress

We will add some progress indicator. For that, Async framework has an interface : IProgress. And one (for the moment) implementation of it : EventProgress.

...

var _progress = new EventProgress();
_progress.ProgressChanged += (s, e) => { Console.WriteLine(String.Format("Progress : {0}", e.Value)); };

...

We create a EventProgress. In the ProgressChanged event, we put a lambda expression showing the progress on the console.

NOTE : There is a shorter way to do it, using :

...

var _progress  = EventProgress.From((r) => Console.WriteLine(String.Format("Progress : {0}", r)));

...

But I had strange exception when using it and, as I read in some forums, it seems to be a bug in the CTP.

Now we just have to pass the EventProgress to the function, as we did with CancellationToken, and call the Report method to give report information as shown in the following example:

class Program
{
    private static CancellationTokenSource _cts;
    private static EventProgress _progress;

    static void Main(string[] args)
    {
        // Create a CancellationTokenSource
        _cts = new CancellationTokenSource();

        // create a new Evenprogress
        _progress = new EventProgress();
        // lambda expression called for every IProgress.Report called
        _progress.ProgressChanged += (s, e) => { Console.WriteLine(String.Format("Progress : {0}", e.Value)); };

        var p = new Program();
        // Pass the token to the function
        p.RunAsync(_cts.Token, _progress);
        Console.WriteLine("Press a key to cancel.");
        Console.ReadKey();
        _cts.Cancel();
        Console.WriteLine("Press a key to quit.");
        Console.ReadKey();
    }

    public async void RunAsync(CancellationToken token, IProgress progress)
    {
        Console.WriteLine("Starting long function.");
        // Pass the token to the function
        await Task.Factory.StartNew(() => LongFunction(token, progress));
        // Check if task was cancelled
        if (token.IsCancellationRequested)
            Console.WriteLine("Long function cancelled.");
        else
            Console.WriteLine("Long function finished.");
    }

    private void LongFunction(CancellationToken token, IProgress progress)
    {
        for (int i = 0; i < 10000; i++)
        {
            Thread.SpinWait (10000000);
            progress.Report(i);
            // Check if cancellation requested. If so, stop processing
            if (token.IsCancellationRequested)
                return;
        }
    }
}

Mandelbrot Silverlight example

 

I made a little Silverlight project showing a simple Mandelbrot fractal using the Async framework.

It is based on another article (to come) about TPL.

You can download it here : AsyncCTPMandelbrotSL.zip

It is pretty straightforward, and is commented. All the details about Mandelbrot, etc. is for another time.

Calling the (a)waiter

Let's have a little look under the hood. Here is what the documentation says about the await pattern:

The expression t of an await-expression await t is called the task of the await expression. The task t is required to be awaitable, which means that all of the following must hold:

· (t).GetAwaiter() is a valid expression of type A.

· Given an expression a of type A and an expression r of type System.Action, (a).BeginAwait(r) is a valid boolean-expression.

· Given an expression a of type A, (a).EndAwait() is a valid expression.

So, it means that to be awaitable, the expression must have a function GetAwaiter() that returns a type that must itself have 2 functions : bool BeginAwait(Action R) and EndAwait().

  • GetAwaiter() just return a type.
  • Beginwait(Action R) returns a bool. If it is true, it means the task in not finished and the execution of the function that call the async function resume. If it is false, it means the task is already finished so the framework doesn?t suspend the execution to resume it immediately.
    Action R is the continuation. It is what will be resumed when the task is done. To put it in another words, it the instructions that are in the async function after the await keyword.
  • EndWait() just returns the result of the task.

The Async CTP already provides Extension methods for the Task class.

If you want to make your own, here it is : (Just for the sake of example, don't try this at work!)

 

class Program
{
    static void Main(string[] args)
    {
        var p = new Program();

        // call async function
        p.WriteTheMessageAsync();

        Console.WriteLine("Press a key to continue.");
        Console.ReadKey();

        // do it again
        p.WriteTheMessageAsync();

        Console.WriteLine("Press a key to quit.");
        Console.ReadKey();
    }

    public async void WriteTheMessageAsync()
    {
        var text = await "This is not the message";
        Console.WriteLine("Message : " + text);
    }

}

static class Extensions
{
    // if done, no need to start task again
    public static bool done = false;

    public static string GetAwaiter(this string thestring)
    {
        Console.WriteLine("**GetAwaiter called");
        // return the waiter. In this case : the string instance itself.
        return thestring;
    }

    public static bool BeginAwait(this string thestring, Action continuation)
    {
        Console.WriteLine("**BeginAwait called");
        // if not done
        if (!Extensions.done)
        {
            Console.WriteLine("**Executing continuation");
            //launch a long task. Then execute continuation.
            Task.Factory.StartNew(() => { Thread.Sleep(5000); continuation(); });
            // it is done
            Extensions.done = true;
            return true;
        }
        return false;
    }

    public static string EndAwait(this string thestring)
    {
        Console.WriteLine("**EndAwait called");
        return "This is the message";
    }
}

Working good, isn't it? So, as you see, the first time it executes the task, and second time it doesn't, as we flagged it was already done. But let's have a look of what the compiler did :

 

public void WriteTheMessageAsync()
{
    d__0 d__ = new d__0(0);
    d__.<>4__this = this;
    d__.MoveNextDelegate = new Action(d__.MoveNext);
    d__.$builder = VoidAsyncMethodBuilder.Create();
    d__.MoveNext();
}

 

public void MoveNext()
{
try
{
    this.$__doFinallyBodies = true;
    if (this.<>1__state != 1)
    {
        if (this.<>1__state == -1)
        {
            return;
        }
        this.t__$await3 = "This is not the message".GetAwaiter();
        this.<>1__state = 1;
        this.$__doFinallyBodies = false;
        if (this.t__$await3.BeginAwait(this.MoveNextDelegate))
        {
            return;
        }
        this.$__doFinallyBodies = true;
    }
    this.<>1__state = 0;
    this.<1>t__$await2 = this.t__$await3.EndAwait();
    this.5__1 = this.<1>t__$await2;
    Console.WriteLine("Message : " + this.5__1);
    this.<>1__state = -1;
    this.$builder.SetCompleted();
}
catch (Exception)
{
    this.<>1__state = -1;
    this.$builder.SetCompleted();
    throw;
}
}
 

Can't recognize my WriteTheMessageAsync function! The magic is now in the MoveNext function.

  • Line 17 : if the state is not 1 or -1, it goes to the await block, that calls the GetAwaiter().
  • Line 24: it calls the BeginAwait(Action r). If it returns true, the function returns. The continuation is passed (MoveNextDelegate) and is executed in the BeginWait(). As the state is set to 1, it will not go to the await block again.  If not, it continues and execute the continuation directly (remember what I said earlier about the return value of GetAwait()).
  • Line 31: Call the EndAwait() to get the result, then execute the continuation.

Conclusion

Very simple, yet powerful, a good companion to the TPL introduced in .NET 4.0

Still a little bit rough, has some bug and can?t make ReSharper working with it so I hope we?ll have a new beta soon (or better, the final version!)

Don't hesitate to take a look here : Async CTP

 

The following two tabs change content below.
Olivier

Olivier

Mobile Engineer at Arcana Studio
Freelance developer. Passionate for mobile Development and IoT. Expert in WinRT and Xamarin. MVP Windows Platform Development Nokia Developer Champion