I’m going to cover a easy example on the new asynchronous programming model in C# 5 and .NET 4.5 with the recent release of Visual Studio 11. The example we will be writing is a small WinForm that will calculate the square root of a large number while providing progress updates to the UI, and moreso, doing all of this asynchronously, and safely.
The first note is that all this can be done right inside our form class (eg. Form1.cs), so I won’t include a ZIP file or source download, the example should be very straight forward and simple. Let’s dig right in.
To start with, we’re going to create a SynchronizationContext. This part is not anything new, and has been around since .NET 2.0 so I won’t be covering it. In short, it’s just a very helpful object that allows you to perform synchronization between threads or other asynchronous environments. Add a field, and initialize it in the constructor.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
m_SynchronizationContext = SynchronizationContext.Current;
}
private SynchronizationContext m_SynchronizationContext;
}
We will be using this to invoke calls to the UI thread safely later on. Next, let’s declare our async method.
private async void ComputeSquareRootAsync(object sender, EventArgs e)
{
double sqrt = await Task<double>.Run(() =>
{
double result = 0;
for (int i = 0; i < 5000000; i++)
{
result += Math.Sqrt(i);
}
return result;
});
}
There are a few things to note here. One, notice the declaration private async void. Here we are telling the compiler that this method will be intrinsically asynchronous, and because the method will contain awaiters, the C# compiler will know to rewrite our method appropriately under the hood.
If you don’t know already, async and await simply work off of the existing Task objects in the framework. By telling the program to await Task.Run, we are saying “Run all code up until this point synchronously, then run the task on a background method, but don’t block the UI, and return control to the caller when the result is returned”. This means that while our Task is running and doing some work off on a background thread, our program will not continue the normal flow of execution until the result is returned, but at the same time will not block the calling context.
Now drag a button onto your form (eg. button1) and set it’s click event to our ComputeSquareRootAsync method. Now drag a label (eg. label1) and a progressbar (eg. progressBar1). Let’s update our method a bit.
private async void ComputeSquareRootAsync(object sender, EventArgs e)
{
label1.Text = "Calculating sqrt of 5000000";
button1.Enabled = false;
progressBar1.Visible = true;
double sqrt = await Task<double>.Run(() =>
{
double result = 0;
for (int i = 0; i < 5000000; i++)
{
result += Math.Sqrt(i);
}
return result;
});
label1.Text = "The sqrt of 5000000 is " + sqrt;
button1.Enabled = true;
progressBar1.Visible = false;
}
Set the label’s initial text in the designer to “Click the button to begin”, and the progress bar’s visiblility to false initially. I also set the button’s text to “Calculate”. This is just cosmetic, but we are making a small, but not really practical, good demo app.
Now what will happen is that the square root will be executed asynchronously while not blocking the UI, but at the same time the last three lines of code that update the controls will not execute until the result is returned (eg. the Task is returned). This is the magic of the new async model.
Let’s implement our progress updates now. To do this, we’re going to implement a new interface onto our form that is provided by .NET 4.5 explicitly for this scenario, and it is called IProgress(Of T).
To make things simple, we will use IProgress(Of Tuple(Of int, int)) (eg. IProgress>) so we can pass in the maximum value and current value of our progress operation (eg. computing the square root).
public partial class Form1 : Form, IProgress<Tuple<int,int>>
Now implement the interface’s Report method.
public void Report(Tuple<int, int> value)
{
DateTime now = DateTime.Now;
if ((now - m_PreviousTime).Milliseconds > 20)
{
m_SynchronizationContext.Post((@object) =>
{
Tuple<int, int> minMax = (Tuple<int, int>)@object;
progressBar1.Maximum = minMax.Item1;
progressBar1.Value = minMax.Item2;
}, value);
m_PreviousTime = now;
}
}
Now you will notice one thing off the bat, I included a reference to a DateTime value named m_PreviousTime. Add this as a field in the Form1 class and set its value to DateTime.Now.
private DateTime m_PreviousTime = DateTime.Now;
You could also do that in the constructor where we initialized our synchronization context. The method is simple. We are creating an anonymous function that passes in an object instance of our Tuple as a parameter, which we explicitly convert through an explicit cast. Through the usage of the Post method, it is actually calling this anonymous function on the synchronization context, which is actually our UI thread, thus giving us a safe UI update and no cross-thread violations. This is similar do doing a Control.Invoke, and I suspect somewhere under the hood it may actually do that but I haven’t looked at the implementation yet.
And now we’re done. Here’s the full Form1 class code so you can make sure you implemented all the steps.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Async {
public partial class Form1 : Form, IProgress<Tuple<int,int>> {
public Form1() {
InitializeComponent();
m_SynchronizationContext = SynchronizationContext.Current;
}
private SynchronizationContext m_SynchronizationContext;
private DateTime m_PreviousTime = DateTime.Now;
private async void ComputeSquareRootAsync(object sender, EventArgs e) {
label1.Text = "Calculating Sqrt of 5000000";
button1.Enabled = false;
progressBar1.Visible = true;
double sqrt = await Task<double>.Run(() => {
double result = 0;
for (int i = 0; i < 5000000; i++) {
result += Math.Sqrt(i);
Report(new Tuple<int,int>(5000000, i));
}
return result;
});
progressBar1.Visible = false;
button1.Enabled = true;
label1.Text = "The sqrt of 5000000 is " + sqrt;
}
public void Report(Tuple<int, int> value) {
DateTime now = DateTime.Now;
if ((now - m_PreviousTime).Milliseconds > 20) {
m_SynchronizationContext.Post((@object) => {
Tuple<int, int> minMax = (Tuple<int, int>)@object;
progressBar1.Maximum = minMax.Item1;
progressBar1.Value = minMax.Item2;
}, value);
m_PreviousTime = now;
}
}
}
}
I may come back and add the source when I have more time.