(Print this page)

Async and Await in .Net
Published date: Sunday, November 24, 2013
On: Moer and Éric Moreau's web site

As I have announced in my article of last month, here is my introduction to the Async and Await features of .Net.

One of the complaint about the Task class I have introduced last month, is that the code is not very linear. I strongly suggest that you (re)read last month’s article as we will build up on what was done in that article.

Async and Await features bring back some linearity while keeping the benefits of asynchronous code making the code more readable. You definitely need to understand task to correctly use Async and Await.

Requirements

To be able to use the Async/Await, you need to target the .Net Framework 4.5 which was introduced with Visual Studio 2012.

The downloadable demo

This month downloadable demo is a solution created with Visual Studio 2013 containing both a VB and a C# project.

The demo project is doing some random calculation (the same as last month article) in a loop so we have some time to test the UI while the computation is processing.

Figure 1: The running demo application

A short example

Before delving into the deep of subject, let’s check a small example to understand what’s going on. Consider this first snippet of code:

Private Sub btnStartSync_Click(sender As Object, e As EventArgs) Handles btnStartSync.Click
    ListBox1.Items.Add("Process starting at " + Now.ToString("HH:mm:ss"))
    ' Call the method that runs synchronously.
    Dim result As String = Wait ()

    ' Display the result.
    ListBox1.Items.Add(result)
    ListBox1.Items.Add("Process ended at " + Now.ToString("HH:mm:ss"))
End Sub

Private Function Wait () As String
    Thread.Sleep(5000)
    Return "Finished"
End Function

If you run this code, the UI thread won’t be responsive for 5 seconds when clicking the button. The reason is that the Sleep method has to be completed before the CPU can run something else. This is synchronous code as you already know. No big surprise here.

We can achieve exactly the same thing (having some code wasting 5 seconds) with this code which looks a lot like the previous one but running asynchronously:

Private Async Sub btnStartAsync_Click(sender As Object, e As EventArgs) Handles btnStartAsync.Click
    ListBox1.Items.Add("Async process starting at " + Now.ToString("HH:mm:ss"))
    ' Call the method that runs asynchronously.
    Dim result As String = Await WaitAsync()

    ' Display the result.
    ListBox1.Items.Add(result)
    ListBox1.Items.Add("Async process ended at " + Now.ToString("HH:mm:ss"))
End Sub

Private Async Function WaitAsync() As Task(Of String)
    Await Task.Delay(5000)
    Return "Finished - async"
End Function

The first thing you need to be aware of is the event handler itself now has the Async keyword in the definition. This tells the compilers that some asynchronous code will be executed in that method.

The second thing to notice is that the event handler is calling a different method (WaitAsync) and it has the Await keyword in front of it.

This WaitAsync method also has the Async keyword in the method declaration and returns a Task (more on this later). Inside that method (WaitAsync), we can find a task that runs asynchronous code (Task.Delay) and once again Await the results.

One thing you have to understand is that when your code is running, every time the Await keyword is met, the code following the Await starts running and the controls returns immediately to the running context. The remaining of the method is not executed as we are awaiting the completion of the code. It is the job of the compiler to keep a reference to where the code is waiting and returns to this reference when the task completes. Meanwhile, the rest of your code can run normally letting you move the form around or start the task again.

That last paragraph is very important to understand because it is the basis of Async and Await. You should have a look at a different explanation by checking “What happens in an Async method” section from http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx.

Awaiting last month Simulation method

If you remember last month’s article, after we launched the task, we had to write some code awaiting that it completes before being able to return to the UI thread to be able to write the output.

In the introduction, I have said that the Async and Await feature brings back some linearity in the code we need to write.

Look at this code (found in the btnStartSimulation click event handler):

Dim count As Integer = Convert.ToInt32(lblCount.Text)
count += 1
lblCount.Text = count.ToString(CultureInfo.InvariantCulture)

btnCancel.Enabled = True

Dim result As String
Try
    result = Await SimulationAsync()
Catch ex As Exception
    result = ex.Message
End Try

ListBox1.Items.Insert(0, result)

' update rest of UI:
count = Convert.ToInt32(lblCount.Text)
count -= 1
lblCount.Text = count.ToString(CultureInfo.InvariantCulture)

If count = 0 Then
    btnCancel.Enabled = False
End If

You will agree that this code really looks like synchronous code. It updates a counter on the screen, enables the cancel button, execute the SimulationAsync method, add the value returned by the method to a listbox, re-updates the counter and disable the cancel if the count is 0.

But there is the magic keyword Await in front of the method call. That means that method will be wrapped as a task and started but the compiler will return the control to the calling context. In this case, the calling context is the form since this code is in a button of the form. That means that at this point the form can be moved around the form, you can push any other button, … and later the Simulation calculation will complete and the compiler will be able to return exactly where it has left (on the Await keyword) and continue to execute the statements of the method. Isn’t cleaner than the code of last month? Even the exceptions are normally handled.

While the Simulation method is exactly the same as last month, I needed something in between the click event handler and the Simulation method. I need something that will transform the synchronous Simulation method into an asynchronous method. This something is a wrapper which by convention will receive an Async suffix. This is my wrapper:

Private Async Function SimulationAsync() As Task(Of String)
    Dim initial As Double = 30
    Dim exercise As Double = 30
    Dim up As Double = 1.4
    Dim down As Double = 0.8
    Dim interest As Double = 1.08
    Dim periods As Long = 30
    Dim sims As Long = 5000000

    Dim token As CancellationToken = m_cts.Token

    ' Run simulation on a separate task to price option:
    Dim T As Task(Of String) = New Task(Of String)(
                                  Function() As String
                                      'Run simulation to price option:
                                      Dim rand As Random = New Random()
                                      Dim start As Integer = Environment.TickCount

                                      'NOTE: we pass cancellation token to simulation code so it can check for
                                      ' cancellation and respond accordingly.
                                      Dim price As Double = Simulation(token, rand, initial, exercise, up, down, interest, periods, sims)

                                      Dim [stop] As Integer = Environment.TickCount

                                      Dim elapsedTimeInSecs As Double = ([stop] - start) / 1000.0

                                      Dim result As String = String.Format("{0:C}  [{1:#,##0.00} secs]", price, elapsedTimeInSecs)

                                      Return result
                                  End Function, token)

    T.Start()
    Await T
    Return T.Result
End Function

This wrapper uses the same code that creates the Task calling the Simulation method we have created last month. This wrapper method requires the Async keyword in the declaration. It is also better if the wrapper returns a Task to better support chaining and error handling. In this case, because the Simulation returns a String, the wrapper returns a Task(Of String). Finally the task is started and we Await the task to run (which returns the control to the caller context). When the task completes, the Result of the task is simply returned.

Cancelling the task

Because the very same Task system is used behind the Await and Async feature, we can reuse the same CancellationToken we used last month to properly handle the cancellation.

Even if you are using Async and Await, you should press CTRL-F5 to test the cancellation otherwise the code will stop even if you have a Try … Catch in place.

Always await for a task

Every Async method you create should be a Function returning a Task or a Task(Of something). It will compile correctly if you create a Sub (or a void method for my C# friends) but that can leads to some strange behavior. If an exception is triggered from an Async Sub (or void method), the way the exception is bubbled up to the caller depends on the type of application you are building (WPF, phone, web, …). In some case, your application will just stop without any chance of catching the exception from the caller.

Some people might have seen the event handlers are Subs (void method) and they are sometimes defined with the Async keyword. Well, that’s the exception. The wrapper that you create yourself should all be Task returning functions.

Conclusion

The syntax of calling asynchronous function is much more readable when using Async and Await than the Task we have done last month but Async and Await still heavily relies on Task so you really need to understand them as you will have to create many tasks before being able to write linear program.

On top of making code more readable, Async and Await also helps fixing the problems of having to get back to the UI thread to assign controls on the forms.


(Print this page)