Asynchronous Programming Using Async Await in .NET

Introduction

 
In my previous article, I talked about modern age computers, which have multiple cores and therefore must be equipped to design and develop applications that can utilize these resources. I also talked about parallel programming to speed up the execution of our programs and looked into the Task Parallel Library (TPL) of the .NET framework to achieve this goal. Today we will investigate another concept: Asynchronous programming using the .NET framework.

What is Asynchronous Programming?

 
I have read a lot about parallel or current programming and then how it has been compared to asynchronous programming. To me, all three terms really mean the same thing.
 
The goal is to run code in parallel to improve the performance without blocking the user interface. In my previous article, I looked at the Task Parallel Library of the .NET framework which allows us to spawn tasks and get work done in parallel. Today, we will investigate the async and await commands which do the same thing but come with a great advantage in ease of use. We will use a simple .NET framework Windows forms application to demonstrate the use of the async and await keywords and compare it with the Task Parallel Library.
 
So, let's begin.
 

Running the Application Synchronously

 
We will create a simple .NET framework Windows forms application, place one button (btnTest) and one label (lblValue) on it, and add the code on the button click event, as shown below:
  1. using System;  
  2. using System.Threading;  
  3. using System.Windows.Forms;  
  4.   
  5. namespace AsyncAwaitNF  
  6. {  
  7.     public partial class Form1 : Form  
  8.     {  
  9.         public Form1()  
  10.         {  
  11.             InitializeComponent();  
  12.         }  
  13.   
  14.         private void btnTest_Click(object sender, EventArgs e)  
  15.         {  
  16.             Thread.Sleep(5000);  
  17.             lblValue.Text = "Value is displayed now";  
  18.         }  
  19.   
  20.     }  
  21. }  
Her,e we see that when we click the button, it will sleep for 5 seconds and then set a text value to the label.
 
When we run the application and click the button, we notice that the screen freezes for 5 seconds and we cannot move the form as the main thread sleeps for 5 seconds. After five seconds the text value is set on the label and the form becomes responsive again. The reason for this is because we are running the code on the main (UI) thread which sleeps for 5 seconds.
 
Now let's use the Task Parallel Library (TPL) and modify this function, shown below: 
  1. using System;  
  2. using System.Threading;  
  3. using System.Threading.Tasks;  
  4. using System.Windows.Forms;  
  5.   
  6. namespace AsyncAwaitNF  
  7. {  
  8.     public partial class Form1 : Form  
  9.     {  
  10.         public Form1()  
  11.         {  
  12.             InitializeComponent();  
  13.         }  
  14.   
  15.         private void btnTest_Click(object sender, EventArgs e)  
  16.         {  
  17.             Task.Factory.StartNew(() =>  
  18.             {  
  19.                 Thread.Sleep(5000);  
  20.             }).ContinueWith(t => {  
  21.                 lblValue.Text = "Value is displayed now";  
  22.             }, TaskScheduler.FromCurrentSynchronizationContext());  
  23.         }  
  24.   
  25.     }  
  26. }  
Now, when we run the application and click the button, we can move the form around and it's responsive. Therefore, while running long processes, we do not need to block the main thread or UI thread, which always makes our application responsive. So, what are we doing here? We simply spawn a new task and inside this task, we sleep for 5 seconds. After 5 seconds, we continue with another task which sets the text on the label. The thing to note here is the option “TaskScheduler.FromCurrentSynchronizationContext()” which brings the context back to the main or UI thread and lets us set the label text value. Really simple. However, if we use the new async await keywords, things can be made much simpler. Modify the code as shown below: 
  1. using System;  
  2. using System.Threading.Tasks;  
  3. using System.Windows.Forms;  
  4.   
  5. namespace AsyncAwaitNF  
  6. {  
  7.     public partial class Form1 : Form  
  8.     {  
  9.         public Form1()  
  10.         {  
  11.             InitializeComponent();  
  12.         }  
  13.   
  14.         private async void btnTest_Click(object sender, EventArgs e)  
  15.         {  
  16.             await Task.Delay(5000);  
  17.             lblValue.Text = "Value is displayed now";  
  18.         }  
  19.   
  20.     }  
  21. }  
We run the application and click the button. The form remains responsive and we can move it around. After 5 seconds, the text value is set in the label.
 
 
Here, we are using the async await keywords. The async keywords just notify that an asynchronous call is coming up in the function. The await keyword makes the task and executes it. However, this does not block the main calling thread. When the task is complete (In this case the Task.Delay), the control comes back to the calling context and the value is set on the label. Therefore, all that was done when we used the Task Parallel Library is done here as well. However, usage is remarkably simple.
 

Summary

 
In this article, we have looked at the async await keywords and how we can use them for asynchronous or parallel programming. We have seen that the use of these keywords simplifies the process compared to using the Task Parallel Library. Should we always use them rather than the TPL? I would say no. Personally, I would use the async and await keywords for IO intensive operations. These are where we are making calls from the UI and want to return to the UI and keep it responsive during the process. Also, we will use them when we have underlying await-able functions to call. On the other hand, when we want to spawn off a process that completes on its own, like completing some processing and writing the results to a database or some log file, I would prefer using the Task Parallel Library.