Introduction to Async and Await in C# 5.0

Introduction

This article provides a brief introduction to use of Async and Await to make simple asynchronous calls from your C# 5.0/.NET 4.5 applications. Async and Await is a new language feature introduced with the .NET 4.5 update.

Async and Await simplifies the code necessary to make asynchronous calls; within the mechanization the compiler manages the complexity whilst the code remains relatively simple. As a developer, you may gain the benefits of this new functionality merely by marking methods using the new keyword "async".

Async and await are still open to the same sorts of problems one may encounter coding for asynchronous processing using other approaches, including deadlocks; the complexity of the code is still there, but it is generated for you and you don't need to write it. Whilst the mechanization simplifies the coding it does still require one to be vigilant towards the proper use of the new features.

Creating Asynchronous Methods

There are three approaches to creating asynchronous methods using the new features; each being defined by its return type:

  • Methods that return void
  • Methods that return Task
  • Methods that return Task<T>

Call Syntax and Error Handling

The async keyword marks a method for the compiler. The await keyword signals the code to wait for the return of a long running operation.

Async calls return almost immediately, leaving the main thread responsive whilst the longer running process is kicked off to run in the background. Use of void returning methods is suitable only for occasions in which the actions completed by the thread are of no further concern to the main thread (for example, if the code is logging an event and the success of the logging task is of no importance to the main thread); void returning methods are suitable for the launch and forget about it sort of scenario. Methods returning Task are similar to void in as much as those methods do not return a value to the calling method either. If data is to be returned to the main thread and handled there, one should use a return type of Task<T>.
Error handling within methods using the three return types is different as well. When dealing with void returning methods, the error handling will need to be accomplished in the async method as it will not return to the calling thread. Similarly, Task return type methods should also handle their own errors. Error handling for methods returning Task<T> may be accomplished within the calling thread.

Rules for use

The following are the rules for use:

  1. The await keyword may not be inserted into a "lock" code block
  2. With try-catch-finally blocks, await may not be used in the catch or finally sections
  3. Async methods may not use "ref" nor "out" parameters
  4. Class constructors may not be marked async nor may they use the await keyword
  5. Await may only be used in methods marked as async
  6. Property accessors may not be marked as async nor may they use await

Example 1 - Void Returning Methods

In this example, the main thread will make a call to an async method with a void return type. In this example, an async method will be used to call a web service to fetch a weather report and display it to a console window. The web service used in this example can be found here: http://www.webservicex.net/globalweather.asmx

All of the code necessary for this example is contained in the Program.cs file; there is a single void return type async method called by main. The async method is static as well. The code is fully commented so you can follow along with it by reading those comments.

The main method calls a void return type async method entitled, "GetGlobalWeather" that accepts a city and country name as its two required arguments. Just to prove the point that the main method is not waiting on the async method to complete, as soon as the call is made, it prints out a message in the console telling the user that the service has been called. If you look at the screen shot in Figure 1, you will see that this message was in fact printed to the screen whilst the service was fetching the weather report, as such, the weather report is actually printed out after the message.

When looking over the following code, notice that the error handling was placed into the async method, it would not be possible to catch and process any errors encountered from within the Main method. When using void returning methods, one should always place the error handling directly within the async method.

The code for this example is as follows:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace SimpleAsyncAwait

{

    class Program

    {

        static void Main(string[] args)

        {

            Console.WriteLine("Starting call to service");

 

            // make a call to a web service using an async method

            GetGlobalWeather("Edinburgh""United Kingdom");

 

            // print out a line to show we are not waiting for the service call to complete 

            // but rather and pressing on with this main thread

            Console.WriteLine("Call in progress..."); 

 

            // wait for the user to finish reading the report

            Console.ReadLine();

        }

 

        // since the return type is void, the method has to handle the data itself - so here 

        // we just dump the data to the screen, error handling has to be implemented 

        // here as well as nothing is returned to the calling thread

        static async void GetGlobalWeather(string city, string country)

        {

            try

            {

                // create an instance of the client

                GlobalWeather.GlobalWeatherSoapClient client = new GlobalWeather.GlobalWeatherSoapClient();

 

                // set a string to the results of the weather query - use await to hold here for the results to

                // be returned to the string variable

                string weather = await client.GetWeatherAsync(city, country);

 

                // once the string is populated, write it out to the console

                Console.WriteLine(weather);

 

                // report completion from the async method

                Console.WriteLine("Service call complete");

            }

            catch (Exception ex)

            {

                // handle any errors here else risk an unhandled exception

                Console.WriteLine("Error:  " + ex.Message);

            }

            finally

            {

                // tell the user to click a key to close the console window

                Console.WriteLine("Press any key to close this window");

            }

        }

    }

}

ConsoleOutput1.jpg

Figure 1 - Console Output for Example 1

Example 2 - Task Returning Methods

In this example, the main thread will make a call to an async method with a Task return type. In this example, an async method will be used to call a web service to fetch a list of cities available for weather reports and display it to a console window. As in the previous example, the web service used in this example can be found here: http://www.webservicex.net/globalweather.asmx

All of the code necessary for this example is contained in the Program.cs file; there is a single Task return type async method called by main. The async method is static as well. The code is fully commented so you can follow along by reading those comments.

The main method calls a Task return type async method entitled, "GetGlobalWeatherCities" that accepts a country name as its required argument. Once the async method is called, note that we can check the status of the operation (amongst other things). Note also that we can press on with the main thread whilst the async method executes; this is illustrated by printing out some nonsense characters to the console window after calling the method.

When looking over the following code, notice too that the error handling was placed into the async method, as with void returning async methods, it is best to handle any errors in the method itself although with the task return type we can monitor and control the thread from main.
 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace AsyncAwait_4

{

    class Program

    {

        static void Main(string[] args)

        {

            // tell the user we are going to list available cities

            Console.WriteLine("List of Available Cities");

 

            // create a task and assign it to the async call

            Task t = GetGlobalWeatherCities("France");

 

            // unlike void, since we have a return type of task, we can do things 

            // with it like check the status of the operation or wait the task. 

            Console.WriteLine("Status:  " + t.Status.ToString());

 

            // To prove the main thread continunes after the call, write some

            // nonsense out the screen whilst the thread runs

            WasteTime();

 

            // hold the console open

            Console.ReadLine();

        }

 

        /// <summary>

        /// Async call to retrieve a collection of cities available for 

        /// weather reports

        /// </summary>

        /// <param name="country"></param>

        /// <returns></returns>

        static async Task GetGlobalWeatherCities(string country)

        {

            try

            {

                // create an instance of the client

                GlobalWeather.GlobalWeatherSoapClient client = 

                    new GlobalWeather.GlobalWeatherSoapClient();

 

                // set up a variable to hold the result and use await to hold for the service

                // to populate it before pressing on with the task

                string cities = await client.GetCitiesByCountryAsync(country);

 

                // once the string variable is populated, print it out to the console

                Console.WriteLine(cities);

            }

            catch (Exception ex)

            {

                // best to handle the exception here if one is encountered

                Console.WriteLine("Error:  " + ex.Message);

            }

        }

 

        /// <summary>

        /// Waste time drawing out some trash to the 

        /// console whilst the async method runs out

        /// </summary>

        static void WasteTime()

        {

            for (int i = 0; i < 50; i++)

            {

                if(i%2==0)

                    Console.Write("-");

                else

                    Console.Write("|");

            }

            Console.WriteLine();

        }

    }

}

ConsoleOutput2.jpg

Figure 2 - Console Output for Example 2

Example 3 - Task<T> Returning Methods

In this example, the main thread will make a call to an async method with a Task<T> return type. In this example, an async method will be used to call a web service to fetch a weather report and display it to a console window. As in the other examples, the web service used in this example can be found here: http://www.webservicex.net/globalweather.asmx

All of the code necessary for this example is contained in the Program.cs file; there is a single Task<T> return type async method called by main. The async method is static as well. The code is fully commented so you can follow along with it by reading those comments.

The main method calls a Task<T> return type async method entitled, "GetGlobalWeather" that accepts a city name and a country name as its required arguments. Once the async method is called, note that we can check the status of the operation as we could with the Task return type. Note also that we can press on with the main thread whilst the async method executes; this is illustrated by printing a date string and a line separator to the console window after calling the method. The Task<T> return type async calls will block on the line that actually handles the returned value, in this case a string containing a weather report.

When looking over the following code, notice that the error handling was placed into Main, a departure from how we handled errors on void and Task returning async methods, you may still handle the errors in the async method if you'd like to do that, but you don't need to if you'd rather not.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace AsyncAwait_3

{

    class Program

    {

        static void Main(string[] args)

        {

            try

            {

                Console.WriteLine("Fetching Current Edinburgh Weather");

 

                // wx is a placeholder that will be populated through the synchronization context once the method returns

                Task<string> wx = GetGlobalWeather("Edinburgh""United Kingdom");

 

                // display the status of the task

                Console.WriteLine("Task Status:  " + wx.Status.ToString());

 

                // even though we called the service, this will execute concurrently whilst the async call is in play

                // and so the date will be displayed before the weather report

                Console.WriteLine(DateTime.Now.ToLongTimeString());

                Console.WriteLine("------------------------------------");

 

                // handled when the value returns - this will block on console.writeline until the result returns

                Console.WriteLine(wx.Result);  

            }

            catch (Exception ex)

            {

                //With Task<T>, we can handle the error in the in the calling thread

                Console.WriteLine("Error: " + ex.Message);

            }

            finally

            {

                Console.WriteLine("Operation complete, press any key to terminate session");

                Console.ReadLine();

            }

        }

 

        /// <summary>

        /// An async call returning Task of type string

        /// </summary>

        /// <param name="city"></param>

        /// <param name="country"></param>

        /// <returns></returns>

        static async Task<string> GetGlobalWeather(string city, string country)

        {

            // note there is no error handling in the async method on this call, that is not to say 

            // one could not put error handling here but in this case it is possible to catch and handle 

            // errors in the calling thread

            GlobalWeather.GlobalWeatherSoapClient client = new GlobalWeather.GlobalWeatherSoapClient();

 

            // we will await the call to fetch the weather and return the result immediately to the caller

            return await client.GetWeatherAsync(city, country);

        }

 

    }

}

ConsoleOutput3.jpg

Figure 3 - Console Output for Example 3

ConsoleOutput4.jpg

Figure 4 - An exception handled in the Main, outside the async method

Summary

In this article, we reviewed the rules for using Async and Await and provided examples of async methods returning void, Task, and Task<T>. The article further addressed some of the issues pertaining to error handling given each of the three potential return types. There is certainly much more to Async and Await than was covered in this simple introduction, and for that matter, with the Task Parallel Library (TPL) in the larger picture.

The article does try to illustrate how simple it is to implement this new language feature to make asynchronous calls within your C# 5.0 applications targeting the .NET 4.5 framework.