A simple example of async and await in C# 5

I have been playing with the Visual Studio 11 developer preview and exploring its asynchronous features, specifically the async and await keywords which are new to C# 5.0. These features have actually been available as a CTP (Community Tech Preview) since October 2010, but I had not found time to try it.

I like to keep examples as simple as possible. I have a Windows Forms application which has a long-running task to perform, and I do not want to lock the UI while it runs. Here is my long-running function:

private int slowFunc(int a,int b)       

{          

System.Threading.Thread.Sleep(10000); 

return a + b;

}

Yes, it takes 10 seconds! I am going to click a button on a form, call the function, and show the result on a label control.

Now, here is how I might try to achieve the goal of not locking the UI using Visual Studio 2010 and C# 4.0:

private void button1_Click(object sender, EventArgs e)

{            

this.button1.Enabled = false; //prevent re-entry 

var someTask = Task<int>.Factory.StartNew(() => slowFunc(1, 2));

this.label1.Text = "Result: " + someTask.Result.ToString(); //oops, blocks calling thread 

this.button1.Enabled = true;       

}

Oops, this did not work at all! The reason is that although I have gone to the trouble of creating a Task in order to run the slow function on a background thread, my work is undone when I call the Result property of the Task object – since this blocks the thread until the Task completes.

Here is how you can fix it in Visual Studio 2010 – remember, there is an easier way in C# 5.0 coming up soon:

private void button1_Click(object sender, EventArgs e)        

{

this.button1.Enabled = false;          

var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext(); //get UI thread context 

var someTask = Task<int>.Factory.StartNew(() => slowFunc(1, 2)); //create and start the Task 

someTask.ContinueWith(x =>     

{                                          

this.label1.Text = "Result: " + someTask.Result.ToString();   

this.button1.Enabled = true;   

}, uiScheduler  

);        

}

This one works. I click the button and the UI does not lock up at all; I can minimize the form, move it around the screen, and so on.

However, I have had to do some extra work. The ContinueWith method tells the Task to run some other code after the background thread has completed. By default this code will not run on the UI thread, which means it will raise an exception when it updates the UI, but you can pass in a TaskScheduler object so that it continues on the UI thread.

Now here is a look at the same problem using C# 5.0. The slowFunc is the same, so I will not retype it. Here is the code for the button click:

private async void button1_Click(object sender, EventArgs e)

{

this.button1.Enabled = false; 

var someTask = Task<int>.Factory.StartNew(() => slowFunc(1, 2)); 

await someTask; 

this.label1.Text = "Result: " + someTask.Result.ToString(); 

this.button1.Enabled = true;

}

Less code, same result, which is usually a good thing.

What is going on here though? First, the async modifier is added to the click event handler. This does not mean that the method runs asynchronously. It means that it contains code that will run asynchronously using await. As Eric Lippert explains, it tells the compiler to rewrite the method for you.

Second, there is the await keyword. I cannot improve on Lippert’s explanation so here it is:

The “await” operator … does not mean “this method now blocks the current thread until the asynchronous operation returns”. That would be making the asynchronous operation back into a synchronous operation, which is precisely what we are attempting to avoid. Rather, it means the opposite of that; it means “if the task we are awaiting has not yet completed then sign up the rest of this method as the continuation of that task, and then return to your caller immediately; the task will invoke the continuation when it completes.

If you refer back to the Visual Studio 2010 examples, you will see that the code is very close to my first, non-working example. In other words,using await makes the code work in the way that intuitively I hoped that it might, without specifically called the ContinueWith method and messing around with the thread context as in the second example.

Page 1 of 2 | Next page

Related posts:

  1. Microsoft WebMatrix released: a simple editor for ASP.NET Razor and more, but who is the target user?
  2. Simple CRUD with Silverlight
  3. Simple CRUD with Silverlight 2.0
  4. Sample code for a very very simple VB database application
  5. Simple ASP.NET performance tips