Async 2 - Understanding the Need for asynchronous programming
How asynchronous programming can help with scalability of you applications
Intro
This post is part of 4 post blog series. In this series i will start with an introduction about the asynchronous programming and then we will explore more details about the asynchronous programming and it benefits then finally we will go through how we can start using asynchronous programming in our applications. This is the Second Post in the series.
Below is the list of all the posts from the series
- What is Asynchronous execution Real life and C# example
- Why you should you consider it as a web developer?(This Post)
- How async/await works Details of StateMachine working
- Demo: Refactoring an application to be asynchronous
Using Threads Efficiently
Let’s make this clear asynchronous programming in not same as creating multiple threads to get the work done in parallel. Multiple threads put the responsibility on the programmer to make sure the code is thead safe and can handle race conditions gracefully.async/await
does not create any new threads. It utilize the current execution thread more efficiently so you do not need to worry about race conditions.
Then the next question is what is the need of the asynchronous programming and how it helps you application with the scalability
?
In applications there are two types of tasks that block the execution threads.
IO Bound Tasks
CPU Bound Tasks
Few Examples of I/O bound Tasks
- Application is waiting for Database query to return results.
- Application is waiting for the response of a Http Call.
- Application is waiting for azure storage SDK to return data.
CPU bound task is when we are waiting for some complex computation to complete.
In web applications the IO Bounds tasks are far too common as compared to CPU bound tasks. So we will see an example how the
Async
code can help with IO Bound task
Lets have a look at the code from last blog post about the Async/Await example lets break down the method the each step of the method
static async Task<int> AsyncOrderCoffee()
{
Console.WriteLine("Attendant: Coffee Ordered received");
HttpClient client = new HttpClient();
Console.WriteLine("Attendant: Ask barista for coffee");
//Step1: Create the task to download contents of a page
Task<string> getStringTask =
client.GetStringAsync("https://msdn.microsoft.com");
//Step2: Wait for the contents to be downloaded
string urlContents = await getStringTask;
Console.WriteLine("Barista: Here is your cappuccino");
return urlContents.Length;
}
Task to download is performed using HttpClient in two steps
-
Step1: Just creates the task and start the download.
-
Step2: Wait for the task to complete.
When we created the task in Step1
it was started automatically and started to download the page contents. Then in Step2
we told the program to not move any further until the contents of the page are downloaded successfully.
How it is different from the normal synchronous flow of execution ?
While the code (HttpClient
) was waiting for remote endpoint to provide the contents of the web page the execution thread was sitting idle so the method released the execution thread control back to the calling method. Keep in mind that code will not create an additional thread but return the control of the existing thread to the calling method.
How it is going to help my application ?
Let’s have a look example of web application hosted using IIS.
ASP.NET processes requests by using threads from the .NET thread pool. The thread pool maintains a pool of threads that have already incurred the thread initialization costs. Therefore, these threads are easy to reuse. The .NET thread pool is also self-tuning. It monitors CPU and other resource utilization, and it adds new threads or trims the thread pool size as needed.
So it will be more ideal to develop applications in a way so that we have to execute to large number of small running tasks instead of few long running tasks. Because then thread pool can reuse the already instantiated thread instead of creating new threads. Instantiating a new thread is a costly operation.
So a very basic request pipeline will look like
So when we call any async methods using await
string urlContents = await getStringTask;
The execution control is returned to the calling method so this return of control to calling method can propagate all the way to the IIS layer and thread can be added back to the thread pool and can perform other work while waiting for some dependency to return results.
This will help us break our application into multiple small running tasks instead of few long running tasks that will help in better utilization of thread pool and we get better scalability for the application as a result.
Where you should use asynchronous ?
Asynchronous programming can help improve most the code we write in everyday life. Few common examples can be
- Database Calls, If you use any ORM like Entity Framework you can refactor you whole repository layer to be asynchronous using
async/await
keywords. - Azure SDK, If you make use of azure SDKs to access azure services like Azure Storage, Azure CosmosDb, KeyVault etc there are
async
variants available for most of the methods you gonna use. - HttpRequest, All the http requests that you have to make from your application using some http client.
- File System IO, If you are accessing file system from your application you can write most of the file system access as asynchronous code
Note: This is just a small list of common use cases but there are many …
I hope with the help of this post i was able to present a case about why every programmer should consider asynchronous programming
for their applications. Writing asynchronous is lot simpler with the help of async/await
keywords.
See you in the next post about the How Async/Await works under the hood. We will look at the IL to understand how async/await
get translated into a State Machine and how state machine implements the asynchronous code flow. This post will be totally optional and contains information that is not needed to use async/await
in your applications.