Async Feature Control Flow (Alan Berman)

VBTeam

What is asynchronous programming?

Let’s say you send out an email, then wait and do nothing for the two minutes it takes to get a response back. You do nothing while you’re waiting. It’s worth waiting because it’ll be back fast. When that happens, sending the email and getting a response is synchronous. But another time you send an email and then forget about it and do something completely different. Three and a half hours later–while you’re engrossed in activity B, Outlook pops up a message that you got your response. That’s asynchronous. You don’t know when the response will be received, and you don’t hang around waiting for it.

In a computer program, most operations are synchronous. Something doesn’t happen until the last operation finished. But there are things that take a long time (in human, not computer terms). These things often include requesting something over the Internet and waiting for a response. If done synchronously, people are waiting around watching swirling donuts on their screen instead of doing their work.

Because this blog is aimed at new users of the Async feature, I’ll avoid mentioning conceptual overhead, architectural impedance mismatch, and leaky abstractions.

When an asynchronous operation is called, the program happily continues on, not caring that something is happening asynchronously off somewhere else. But, when the asynchronous operation completes, a callback function is called out of the blue–and the response is handled. That may happen a long time later.

So what’s the problem?

It all works fine. But the callback function is usually somewhere else in the program. The calling code and response code are in different places. That makes it difficult to make your code look beautiful. For coders, it’s difficult to use control structures like Do While. It gets worse when you start adding error handling with Try Catch blocks. While it all works, it blows your control flow. It isn’t pretty.

What’s the solution?

Async is a new feature being developed that installs with a CTP release on top of Visual Studio 2010. Async doesn’t blow your control flow, so your code looks beautiful again. But it helps to understand what happens when you use it.

Examples

Look at this code, along with the carefully crafted code comments. The goal is to understand that calling an Async method (DownloadStringTaskAsync) returns a task. And the subsequent Await statement immediately exits the method (returning another task), waits for the original task to complete, returns the result of the original Async method (DownloadStringTaskAsync), and then continues within the method.

Imports System.Threading.Tasks
Imports System.Net
Module Module1
    Sub Main()
        GetWebPageAsync("http://msdn.microsoft.com")
        Console.WriteLine("In Main after Async call")
        Console.ReadKey()
        '     Output:
        '      In GetWebPageAsync before Await - task 1
        '      In Main after Async call
        '      In GetWebPageAsync after Await - task 1
    End Sub
    Private Async Function GetWebPageAsync(ByVal webAddress As String) _
        As Task(Of String)
        Dim client As New WebClient()
        ' Start an Async task.
        Dim theTask As Task(Of String) =
            client.DownloadStringTaskAsync(New Uri(webAddress))
        ' Await the task. This is what happens:
        ' 1. Execution immediately returns to the calling method, returning a
        '    different task from the task created in the previous statement.
        ' 2. When this task completes, the result from the DownloadStringTaskAsync
        '    method is returned by the Await statement, and execution continues
        '    within this method.
        Console.WriteLine(
            "In GetWebPageAsync before Await - task " & theTask.Id.ToString)
        Dim result As String = Await theTask
        Console.WriteLine(
            "In GetWebPageAsync after Await - task " & theTask.Id.ToString)
        Return result
    End Function
End Module

This example does the same thing as the last example. But two statements are contracted into one. Just remember that it does the same thing as the last example. You may see things written this way.

    Private Async Function GetWebPageAsync2(ByVal webAddress As String) _
        As Task(Of String)
        Dim client As New WebClient()
        ' Start an Async task and await the task.
        ' This is a condensed version of the code in GetWebPageAsync().
        Dim result As String =
            Await client.DownloadStringTaskAsync(New Uri(webAddress))
        Return result
    End Function

The following example has an additional Async method. The thing to get out of it is that the task returned by an Await statement is different from the task used by the Await statement.

In the GetWebPageAsync function, “Await theTask” immediately returns a different task. In the ProcessWebPageAsync function, that different task is returned in “Dim theTask As Task(Of String) = GetWebPageAsync(…)”.

Imports System.Threading.Tasks
Imports System.Net
Module Module1
    Sub Main()
        ProcessWebPageAsync("http://msdn.microsoft.com")
        Console.WriteLine("In Main after Async call")
        Console.ReadKey()
        ' Output:
        '   In GetWebPageAsync before Await - task 1
        '   In ProcessWebPageAsync before Await - task 2
        '   In Main after Async call
        '   In GetWebPageAsync after Await - task 1
        '   In ProcessWebPageAsync after Await - task 2
    End Sub
    Private Async Function ProcessWebPageAsync(ByVal webAddress As String) _
        As Task(Of String)
        Dim theTask As Task(Of String) = GetWebPageAsync(webAddress)
        Console.WriteLine(
            "In ProcessWebPageAsync before Await - task " & theTask.Id.ToString)
        Dim result As String = Await theTask
        Console.WriteLine(
            "In ProcessWebPageAsync after Await - task " & theTask.Id.ToString)
        Return result
    End Function
    Private Async Function GetWebPageAsync(ByVal webAddress As String) _
        As Task(Of String)
        Dim client As New WebClient()
        ' Start an Async task.
        Dim theTask As Task(Of String) =
            client.DownloadStringTaskAsync(New Uri(webAddress))
        ' Await the task. This is what happens:
        ' 1. Execution immediately returns to the calling method, returning a
        '    different task from the task created in the previous statement.
        ' 2. When this task completes, the result from the DownloadStringTaskAsync
        '    method is returned by the Await statement, and execution continues
        '    within this method.
        Console.WriteLine(
            "In GetWebPageAsync before Await - task " & theTask.Id.ToString)
        Dim result As String = Await theTask
        Console.WriteLine(
            "In GetWebPageAsync after Await - task " & theTask.Id.ToString)
        Return result
    End Function
End Module

Disclaimer
The above code examples are abbreviated, and are meant to convey initial understanding and not meant to strongly, subtly, or otherwise recommend any coding pattern or style whatsoever.

Technical Q&A

Why is the task returned by Await different from the task in the Await statement?
The task returned by Await includes code execution that occurs after the Await within the same method.

Does the DownloadStringTaskAsync method use any threads while it’s happening?
It does not allocate any additional threads. It uses the existing I/O completion thread briefly at the end.

Why do I care if it uses a thread?
An extra thread here and there doesn’t make much difference. But if you need a lot, you can run out of them, or maybe switching between them becomes inefficient. With multithreading, you also need to worry about synchronization of access to common states and resources. This is especially the case when a background thread needs to access the user interface thread.

I use “Go to Definition” all the time, but the F12 key doesn’t work for it.
I understand your concern, even though it’s off topic. Press the button on the keyboard that toggles between the special purpose keys and function keys.

What’s the difference between asynchronous programming and using multiple threads to do parallel programming?
In synchronous programming, something doesn’t happen until the last thing is done. There may not be any human delays for which Async may be useful, but there are much shorter delays. Think milliseconds. These often are I/O operations like waiting to retrieve something from the hard drive. There are a lot of short delays, so the milliseconds add up to mega-milliseconds, so people watch swirling donuts.

Using multiple threads and the Task Parallel Library can improve things. On a single core processor, while one thread is waiting to get something back from the hard drive, Windows may assign another thread to run on the processor. That makes the whole thing run faster. On multiple core processors, things are even better. Windows may assign one thread to run on one core, and another to run on another.

How do I try Async?
Install the Visual Studio Async CTP (SP1 Refresh) on top of Visual Studio 2010 SP1. In your project, remember to add a reference to AsyncCtpLibrary.dll, which is in My Documents\Microsoft Visual Studio Async CTP\Samples.

Thanks to Lucian Wischik and Lisa Feigenbaum for providing tech review.

Details and Download Page  |  Forum for Feedback and Questions  |  Microsoft Connect for Bugs and Suggestions

0 comments

Leave a comment

Feedback usabilla icon