Learning SynchronizationContext, async, and await

Understand the core concepts behind asynchronous programming to build applications that are responsive

Asynchronous programming

Asynchronous programming

Asynchronous programming is a form of parallel programming that enables you to execute tasks separate from the main application thread and then notifies the thread when its execution is over. Asynchrony helps you to execute tasks sans the need of holding up the execution flow or responsiveness of your application.

Microsoft has provided support for parallel programming in .Net Framework to leverage the benefits of multi core systems. You can leverage asynchrony to improve the performance and responsiveness of your application.

Essentially, there are two possible types of operations in an application. These include compute - bound and I/O bound operations. Compute - bound operations are those in which the computation can be performed on a separate thread so that the main thread can continue with its execution. On the contrary, I/O bound operations are those in which that are executed externally and hence they don't need to block the current thread while the I/O is in progress.

Synchronization context and execution context

Every thread has a context associated with it -- this is also known as the "current" context -- and these contexts can be shared across threads. The ExecutionContext contains relevant metadata of the current environment or context in which the program is in execution. The SynchronizationContext represents an abstraction -- it denotes the location where your application's code is executed.

A SynchronizationContext enables you to queue a task onto another context. Note that every thread can have its own SynchronizatonContext. The SynchronizationContext class has been added recently to the System.Threading namespace and facilitates communication between threads. You can read more about SynchronizationContext and ExecutionContext here.

A deep dive inside Async and Await

The three asynchronous programming patterns include the following:

  1. Asynchronous Programming Model (APM)
  2. Event-based Asynchronous Pattern (EAP)
  3. Task-based Asynchronous Pattern (TAP)

The latest, the recommended and also the most elegant of these all is the TAP.

Note that you can mark a method using the "async" keyword that returns void, Task, or Task<TResult>. Note that when an exception occurs inside an asynchronous method that has a return type of Task or Task<T>, the exception details are stored inside the Task instance.

On the contrary, when an exception occurs inside an asynchronous method that has a return type of void, the exception details are stored inside the SynchronizationContext that was active at the time when the asynchronous method was called. In essence, you cannot handle exceptions raised within an asynchronous method having a return type of void using exception handlers written inside the asynchronous method. Because of the different computing and error handling semantics, it is advisable to avoid asynchronous methods having void return types unless there is a sufficient reason to use them.

When you use the "await" keyword inside an asynchronous method, the method is split up inside a state machine. Note that the "await" keyword captures the current SynchronizationContext and as soon as the task that has been awaited using the "await" keyword is complete, the state machine is resumed and execution of the code in the caller method restarts -- this is also known as continuation. If execution of the code that has been awaited using the "await" keyword has been completed at the time when the suspension point is encountered, the asynchronous method (the method that has been marked as "async") executes synchronously. If the execution of the code that has been awaited isn't complete, a continuation delegate is attached to the code that has been awaited.

You can take advantage of asynchronous methods that return void to create asynchronous event handlers. The Main method cannot be marked with the "async" keyword as it is the application's entry point -- an "async" Main method would terminate the moment it is called. The "await" keyword informs the compiler that the method can have a suspension and resumption point. Incidentally, you can use the "await" keyword only on a method that has been marked as asynchronous using the "async" keyword.

An async method when called, runs synchronously on the current thread irrespective of the return type of the method. When you mark a method as asynchronous using the "async" keyword, you just inform the compiler that the method can be split into multiple tasks -- some of these tasks may execute asynchronously. Also, the inclusion of the “async” keyword in a method doesn’t queue the method invocation as a part of the thread pool. The asynchrony (i.e., whether a method would have asynchronous behavior) actually depends on the suspension point you have mentioned in your method using the "await" keyword. If you don't include the "await" keyword inside an asynchronous method, the entire method would execute synchronously.

This article is published as part of the IDG Contributor Network. Want to Join?

From CIO: 8 Free Online Courses to Grow Your Tech Skills
Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.