Click here to Skip to main content
15,880,651 members
Articles / Programming Languages / C#

Asynchronous Programming. Getting to grips with Async and Await.

Rate me:
Please Sign up or sign in to vote.
4.92/5 (46 votes)
25 Sep 2016CPOL10 min read 41.6K   676   106   8
How to use the async/await keywords in .NET4.5

Introduction.

The async and await keywords were introduced in Visual Studio 2012 with the aim of making the management of asynchronous methods more simple. This piece shows how best to use these keywords in order to produce efficient code that’s free of the many elusive bugs that inhabit the world of asynchronous programming.

Preventing the UI Thread from Blocking

A common situation is that you have a long-running method and you want the user controls to remain responsive while it is running and to be updated by the returned value of the method when it completes. The method is started by raising a button click event. This is what you can do.

C#
private async void ReadWebPageEventHandlerAsync(object sender, RoutedEventArgs e)
      {
          string text ;
          using (var client = new HttpClient())
          {
              text = await client.GetStringAsync(@"http://www.codeproject.com/");
          }

          //control returns here when GetStringAsync finishes,
          //Do something with the result

          this.textBox.Text = text;
      }

The method's signature matches that of the event handler but is preceded by the async modifier and, by convention, the method name ends in async. The presence of the keyword async does not mean that the method will run asynchronously. It means that the method has the ability to manage an asynchronous process. It is the await keyword that initiates that process. Without the await statement, the method will run entirely synchronously on the UI thread. Here’s a graphic.

Image 1

It looks a bit like the solution to Hampton Court Maze but it’s supposed to show the progress of the UI thread through the async method. The method is run in two stages. The code before the await statement runs first on the UI thread. At the await statement, the asynchronous process is started and the UI thread exits the method. When the asynchronous process ends, the UI thread returns, retrieves the payload and runs the rest of the method. It then goes merrily on its way. The value return from HttpClient.GetStringAsync is of type Task<T>, where T, in this case, is a string. The Task entity represents the asynchronous method, it has various management functions and debugging aids. The important point is that, when it is awaited, it returns the payload T. The method is truly asynchronous, a thread is used to start it and to retrieve the data when it finishes but for the rest of the time, there is no thread standing around, idle, twiddling its thumbs waiting for data to arrive from cyberspace.

A Little Bit about Threading

The async await keywords provide enough abstraction for you not to have to work at thread level directly. There is certainly no need to martial the threads yourself, leave all that stuff to the async method. You don’t want to be cooking food when you have your own chef. But it is useful to know a little about threading in order to code efficiently. There are two types of thread, foreground threads and background threads. Foreground threads are the main executing threads of the application, when they end the application ends. In UI applications, the foreground thread is the UI thread. Background threads can come and go as they please, they usually reside in the threadpool. The threadpool is a dynamic collection of readymade threads that are commonly used to do work that has been offloaded from the foreground threads. Running tasks on background threads when the processor is fully loaded and all cores are working does not increase efficiency. All that happens is that they are given a time-slice of the processor cycle in which they can run. So, in this situation, time is not saved, it's just split between tasks. This sort of arrangement is called parallel processing. Threads are expensive in terms of memory and their garbage collection is protracted, so it’s best to use them sparingly and to make sure that they are fully occupied when they are running. As you can see from the image below, the threadpool is fairly labour intensive.

Image 2

Converting a Synchronous Method to Run Asynchronously

The first example used the HttpClient.GetStringAsync method. This is a truly asynchronous method, most of the processing is done outside of the CPU on a server and no threads are used to run it. When you convert a synchronous method to an asynchronous one, it will run on the CPU from a threadpool thread. So, in a sense, it is a fake asynchronous method as it runs on a background thread. To convert, do this:

C#
int result= await Task.Run(()=>MyLongRunningMethod());

If you wrap the Task.Run call inside another async method that simply calls Task.Run, the extra await statement will degrade performance. Also, calls to methods ending in Async should be reserved for truly asynchronous methods. So doing this sort of thing is not recommended.

C#
private async Task<int> MyLongRunningMethodAsync()
{
return await  Task.Run(()=>MyLongRunningMethod());
}

Writing a Truly Asynchronous Method

To write an Asynchronous method that does not run on the threadpool, you need to be able to subscribe to an asynchronous event that fires when the method completes. The system.Timers.Timer runs asynchronously and has such an event, so it’s possible to demonstrate the technique using this timer, but, in the real world, you would be interfacing with an external event.

C#
public static Task<int> ProveRiemannsHypothesisAsync()
     {
         int result = 42;
         var taskCompletionSource = new TaskCompletionSource<int>();
         var timer = new Timer(1500);
        //subscribe to the asynchronous event
         timer.Elapsed += (sender, e) =>
         {
             timer.Stop();
             timer.Dispose();
             //result would usually be set using an event args property
             taskCompletionSource.SetResult(result);
         };
         timer.Start();

         return taskCompletionSource.Task;
     }

Dealing with Exceptions

With asynchronous methods, any exceptions raised are not thrown until the method’s result is returned at the await statement. So the try block needs to be around the await. In the common situation where one async method calls another async method, the exception will ‘bubble up’ to the parent method.

C#
static async Task<string> ReadFileAsync(string filename)
      {
          using (var reader = File.OpenText(filename))
          {
              return await reader.ReadToEndAsync();
          }
      }

      public static async Task MainAsync()
      {
          string fileContents;
          try
          {
            fileContents=  await ReadFileAsync(@"C:\Temp\missingFile.txt");
          }
          catch (IOException e)
          {
              Console.WriteLine("Caught IOException: {0}", e.Message);
          }
      }

Async methods that return void cannot be awaited and, if they raise an exception, they will take down the application. So it’s a good idea to reserve async void methods for event handlers where you have no choice but to return void. It’s ok to change a signature from async void to async Task, the compiler will handle the conversion. There is no need to new up a Task. A Task is best generated by calling an asynchronous method because what you get back is a hot Task, a Task that’s on active duty. The constructors generate cold Tasks. Cold Tasks are like cold soup – not easily consumed.

Cancelling Asynchronous Methods

Managing the cancellation of asynchronous method takes place by way of the CancellationTokenSource class. Methods that support cancellation take the Token returned from the CancellationTokenSource.Token property as a parameter. Calling CancellationTokenSource.Cancel(), cancels the method and calling CancellationTokenSource.CancelAfter(TimeSpan, timeSpan) will timeout the method when the timeSpan has passed. The cancellation should usually result in an OperationCanceledException being thrown to indicate that the Task was cancelled and did not just finish normally. Cancellation is not automatic, the cancellable method needs to check if the CancellationToken.IsCancellationRequested property has been set to true. The CancellationToken is spent once the IsCancellationRequested property is set to true, so a new instance of CancellationTokenSource is required to deal with the next cancellation request.

Reporting Progress

The Progress<T> class is used to report progress, where T is the type that indicates the progress made. It’s best to use a value or an immutable type because the reporting of progress is itself asynchronous. If you use a mutable reference, you will find that the async gremlins will have fun changing its value behind your back.

C#
private readonly Progress<int> progressReporter; 
private CancellationTokenSource cancellationTokenSource;

public MyViewModel()
{
 progressReporter = new Progress<int>();
   //subscribe to the ProgressChanged Event.
   progressReporter.ProgressChanged +=
    //  Update the  ProgressPercent property when it fires
        ((sender, percent) => ProgressPercent = percent);
  cancellationTokenSource = new CancellationTokenSource();
}

private int MyLongRunningMethod( CancellationToken ct,IProgress<int> progress=null)
        {
            int i = 0;
            while (i < 100)
            {
                Thread.Sleep(50);
                i++;
                if (progress != null)
                {
                    progress.Report(i);
                }

                ct.ThrowIfCancellationRequested();
            }
          
            return 42;
        }

The SynchronizationContext and Console Applications.

The thread that the continuation part of an async method runs on depends upon the SynchronizationContext of the original thread that called the async method. The SynchronizationContext is a management entity that ensures that work posted to it is run in the correct context. With the button click example, the invoking thread will be the UI thread and that thread’s SynchronizationContext runs work on the UI thread. What happens is that the SynchronizationContext is captured at the await statement. When the awaited method completes, the code after the await statement, (the continuation), is converted into a delegate and posted to the captured SynchronizationContext. That context then runs the delegate on the UI thread. With Console apps, the ‘Main’ thread’s SynchronizationContext runs work on the threadpool. So, even if the main thread is blocked, the continuation will be executed. This behaviour means that, to test methods designed to be run on a UI thread, you need to set the main thread’s SynchronizationContext to one that mimics that of a UI thread and runs both parts of an async method on the same thread. The downloadable example shows how to arrange this. The following code will run ok without a UI thread but will block with one.

C#
        private static void Main(string[] args)
        {
            //start the task
            MainAsync(args);
            Console.WriteLine("Waiting for MainAsync to finish.");
            // this blocks the thread
            Console.ReadLine();
        }
public static async Task MainAsync(string[] args)
        {
           //wait 3 seconds
            await Task.Delay(TimeSpan.FromSeconds(3));
           //this runs ok on a threadpool thread
           // but not with a UI thread
           // the ReadLine method in Main will block it 
             Console.WriteLine("MainAsync  has finished\nPress return to end");
        }

Using the ConfigureAwait Extension Method

It is possible to change the default behaviour of an async method so that the continuation part of the method always runs on the threadpool instead of the SynchronizationContext of the thread that invokes the method. All you need to do is add the extension ConfigureAwait(false) to the awaited method. You can improve performance by employing this technique but it’s not a good idea to use it when updating UI controls. They need to be set on the thread that created them.

C#
public static async Task AsyncMethod()
     {
             Needle needle = await LookForNeedleInHaystackAsync().ConfigureAwait(false);
             Console.WriteLine("Found a size {0} needle", needle.Size);
     }

Unit Testing Asynchronous Methods

The basic technique for unit testing asynchronous methods is to write a test method with a signature that returns a Task and is prefaced by the async modifier. There should always be an await statement in the body of the test method that invokes the particular async method under test. Something like this:

C#
[TestMethod]
        public async Task LookForNeedleInHaystackAsyncReturnsSize10Needle()
        {
            Needle needle = await Program.LookForNeedleInHaystackAsync();
            Assert.AreEqual(needle.Size, 10);
        }

Exceptions can be tested by using the ExpectedException attribute and simply awaiting the method under test.

C#
[TestMethod]
        [ExpectedException(typeof(ArgumentException))]
        public async Task LongRunningAlgorithmAsyncThrowsArgumentExceptionWhenSeedIsZero()
        {
            await Program.LongRunningAlgorithmAsync(0);
        }

It’s not a good idea to allow the method under test to call non CPU bound code in its await statement. These sorts of calls depend, to a greater or lesser extent, upon unpredictable external devices that can take an inordinate amount of time to complete. The trick here is to replace the call with one that invokes a substitute method that runs entirely on the CPU. There are two main approaches to doing this. The first is to construct the substitute so that it returns a completed Task using Task<T>.FromResult(T instance). This will run synchronously.

C#
//Using NSubstitute;
var SubstituteForDataAcccessLayer = Substitute.For<IDataAcccessLayer>();

//This runs synchronously and returns a completed Task<int> with a payload of 4     
SubstituteForDataAcccessLayer.GetStockLevelAsync(0).ReturnsForAnyArgs(Task.FromResult(4));

The second approach is to have the mock object's method behave more like an asynchronous method by getting it to post a continuation to the captured SynchronizationContext's dispatcher queue . This can be quite useful as some elusive bugs can be caused by continuations running at a point in the application that you didn’t anticipate.

C#
//This mocks an asynchronous method by posting a continuation to
//the captured SynchronizationContext's dispatcher queue

//Using NSubstitute;

SubstituteForDataAcccessLayer.GetStockLevelAsync(0).ReturnsForAnyArgs(
   async x =>
            {
              await Task.Yield();
              return 4;
            });

Debugging Async Methods

It can be useful, when debugging, to employ a method that reports the nature of the current thread and the state of the threadpool at various points in the application. Something like this:

C#
public static void ShowThreadInfo(string origin)
     {
         int workerThreads;
         int completionPortThreads;
         ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads);
         Console.WriteLine(
             "Location: {0}\n Current thread is {1}.
                        Id {2}\nThreadpool threads available {3}\n",
             origin,
             Thread.CurrentThread.IsThreadPoolThread ? "Threadpool" : "Foreground",
             Thread.CurrentThread.ManagedThreadId,
             workerThreads);
     }

You can then display this sort of information in a Console window.

Image 3

It would appear, from the display, that StreamReader.ReadToEndAsync() is utilizing a threadpool thread.

Sending Concurrent Requests to a Server

This technique sends multiple requests to the server, asynchronously. Then it uses an await statement that doesn’t return until all the data has been up loaded. Here’s an example using Pizzas that are accessed by their order number.

C#
public static async Task<List<Pizza>> LoadPizzasAsync(IEnumerable<int> orderNumbers)
        {
            var queue = new Queue<int>(orderNumbers);
            var tasks = new List<Task<Pizza>>();
            while (queue.Count > 0)
            {
                int orderNumber = queue.Dequeue();
                //start each task off but don't await it here
                //tasks are started by simply invoking the async method
                Task<Pizza> task = LoadPizzaFromServerAsync(orderNumber);
                tasks.Add(task);
            }
          
            //await for all tasks to complete
            Pizza[] loadedPizzas = await Task.WhenAll(tasks);
            return loadedPizzas.ToList();
        }

As this runs asynchronously, the chances are that the returned Pizzas will not be in the same sequence as they were when ordered. To avoid disappointment, it’s best to sort them before dishing them out.

Image 4

Throttling Concurrent Requests to a Server.

In the previous example, the server could become overloaded with concurrent requests as all the requests were sent at once. In this example, the number of requests is limited (throttled) to the maximum concurrency level of the server. The following code is based on this excellent presentation by Mads Torgersen.

C#
public async Task<List<Pizza>> 
       LoadPizzasAsync(IEnumerable<int> orderNumbers, int maxConcurrency)
        {
            int throttlingLevel = maxConcurrency;
            var tasks = new List<Task>();
            var pizzaList = new List<Pizza>();
            var queue = new Queue<int>(orderNumbers);

            int concurrentCalls = 0;
            while (concurrentCalls < throttlingLevel && concurrentCalls < queue.Count)
            {
                //start the number of tasks up to the maximum concurrency level
                tasks.Add(this.GetPizzaAsync(queue, pizzaList));
                concurrentCalls++;
            }

            await Task.WhenAll(tasks);
            return pizzaList;
        }

 private async Task GetPizzaAsync(Queue<int> queue, List<Pizza> pizzaList)
        {
            while (queue.Count > 0)
            {
                int orderNumber = queue.Dequeue();
                Pizza pizza = await this.LoadPizzaFromDatabaseAsync(orderNumber);
                //As each task completes, add the result to the list
                //if there are any tasks left in the queue, start the next task
                pizzaList.Add(pizza);
            }
        }

Conclusion

Harnessing the full power of multi-cored processors depends upon keeping the cores fully occupied. It’s hoped that the information given here will help you put them through their paces without falling into the many elephant traps that await the asynchronous coder. Sorry about the pun, I tried to resist it but couldn't.

References

History

  • 20th May, 2016: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Student
Wales Wales
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionPlease show good practices Pin
Dave Elliott17-Oct-16 6:53
Dave Elliott17-Oct-16 6:53 
Using List<T> or Queue<T> in an asynchronous method is bad and will generate exceptions as they are not thread safe. Use ConcurrentQueue<T>.

Also calling Count() at any point in time whether synchronous or asynchronous is bad as well and is not performant. Use the Linq method Any().
GeneralRe: Please show good practices Pin
George Swan25-Apr-17 12:02
mveGeorge Swan25-Apr-17 12:02 
QuestionAbout the pictures Pin
Nelek25-Sep-16 20:37
protectorNelek25-Sep-16 20:37 
AnswerRe: About the pictures Pin
George Swan26-Sep-16 3:18
mveGeorge Swan26-Sep-16 3:18 
GeneralRe: About the pictures Pin
Nelek26-Sep-16 20:10
protectorNelek26-Sep-16 20:10 
PraiseVery nice! Pin
Your Display Name Here10-Jun-16 6:17
Your Display Name Here10-Jun-16 6:17 
GeneralRe: Very nice! Pin
George Swan10-Jun-16 20:26
mveGeorge Swan10-Jun-16 20:26 
GeneralRe: Very nice! Pin
Your Display Name Here11-Jun-16 17:53
Your Display Name Here11-Jun-16 17:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.