.NET 4.5 Async “ConfigureAwait” – Keep the Synchronization Context in Mind

December 1, 2012

tags: ,
one comment

I assume you have heard about the great addition in .NET 4.5 – The Async Keyword.

It is truly one of the key changes made to the .NET language, and it is awesome.
Asynchrony should be part of our code, there’s no escape from that.

  • If you write UI applications – you need asynchrony to avoid blocking the UI thread.
  • If you make I/O operations – you should use its asynchronous model that utilizes the I/O completion threads instead of occupying a worker thread.
  • If you build a service – you may want to implement the service using the asynchronous approach if you’re doing mainly I/O work.
  • If you want to parallelize your code – you may want to use the TPL and invoke operations asynchronously and in parallel.
  • And more..

Prior to .NET 4.5, the code looks quite awful in terms of all the nested lambda expressions, error handling, and the use of the synchronization context.
It was also easy to make mistakes writing in this fairly complex paradigm, but I guess it comes down to the developer’s maturity and habits. I specifically got quite used to that by now, so it is all readable to me Smile.

In .NET 4.5, Microsoft introduced us with the new ‘async’ and the ‘await’ keywords.
The goal of this post isn’t to explain what it does, there are enough resources out there already that you can follow up on if you like to catch up.

I would like to discuss a specific feature of the ‘await’ keyword, which is continuing the execution on the captured synchronization context.
When you write the following code –

Code Snippet
  1. async void Do()
  2. {
  3.     Console.WriteLine("Thread Id: {0}", Thread.CurrentThread.ManagedThreadId);
  4.  
  5.     await DoSomethingAsync();
  6.     
  7.     Console.WriteLine("Thread Id: {0}", Thread.CurrentThread.ManagedThreadId);
  8. }
  9.  
  10. private Task DoSomethingAsync()
  11. {
  12.     return Task.Run(() =>
  13.         {
  14.             Thread.Sleep(1000);
  15.  
  16.             Console.WriteLine("Thread Id: {0}", Thread.CurrentThread.ManagedThreadId);
  17.         });
  18. }

Q: What would be printed in line 7? Would it be the same as in line 3?

That’s a trick question Smile
Using the ‘await’ keyword at its default form results in the continuation to be posted to the synchronization context available before beginning the asynchronous work.
This means that the output of line 7 relies on the implementation of the synchronization context.
If you’re inside a WPF application, the DispatcherSynchronizationContext will post it to the same thread, so line 7 would end up printing the same Id as in line 3.
However, if you’re in environment where there isn’t such synchronization implementation, such as the default .NET ‘SynchronizationContext’ which simply queues the work to the thread pool, you should end up with something else.

This question was just for fun though, the key thing I wanted to emphasize is –
Posting the continuation to the captured synchronization context is wasteful in case you don’t care about it.

That is especially true if you’re in a UI application, where the continuations are made on the UI thread, and you may not even need access to UI resources at all.

Furthermore, even in case your code executes inside a synchronization implementation where it just queues it to the thread pool, there’s still some performance hit, and though it is fairly minimal, if you can modify your code to be context agnostic and basically work as efficient as possible, you should do it.

In conclusion, in case you don’t care about the synchronization context, you should use the ‘ConfigureAwait’ method on the await and switch it off.
The above code could be fixed as following – (The change is at line 5)

Code Snippet
  1. async void Do()
  2. {
  3.     Console.WriteLine("Thread Id: {0}", Thread.CurrentThread.ManagedThreadId);
  4.  
  5.     await DoSomethingAsync().ConfigureAwait(false);
  6.     
  7.     Console.WriteLine("Thread Id: {0}", Thread.CurrentThread.ManagedThreadId);
  8. }
  9.  
  10. private Task DoSomethingAsync()
  11. {
  12.     return Task.Run(() =>
  13.         {
  14.             Thread.Sleep(1000);
  15.  
  16.             Console.WriteLine("Thread Id: {0}", Thread.CurrentThread.ManagedThreadId);
  17.         });
  18. }

There’s a demo project if you like to download.
Just make sure you use the WPF application in case you wish to see that the thread ID is the same as line 3 when not switching the feature off.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

one comment

  1. deecytheoftApril 20, 2013 ב 14:23

    When I originally commented I clicked the -Notify me when new comments are added- checkbox and now each time a comment is added I get 4 emails with the exact same comment. Is there any way you’ll be able to take away me from that service? Thanks!

    [url=http://www.michaelkorsonline.com]michael kors bags sale[/url]

    Reply