About
In this code snippet, we will take a look at async and await in C#.
The aysnc keyword enables a method to use the await keyword. These two keywords together enable us to execute code asynchronously. When called an async method will return a Task.
A Task can be thought of as a “unit of work”. You can take a method and “make it/put it” into a Task. This task can then be awaited at a later point in your code allowing you to execute other code in the meantime. You can also make multiple tasks and execute them in parallel and then wait for the results from them. I covered this in a post about the TPL(Task Parallel Library) here.
If you take a look at this post I made about threads you will see that if there is a time-consuming task it will block the execution of the main thread(unresponsive UI) until it is done executing. This can be solved by either creating a new thread for the task in question or using async/await to execute the code asynchronously without starting a new thread(all multithreaded/parallel code can technically be considered asynchronous too).
Generally, a thread should be created to help with CPU bound operations while using asynchronous execution(without spawning a thread) should be used for I/O bound operations.
I/O bound: Operations that won’t benefit from parallel execution such as reading a file or making a network request. While using threads, in this case, will help resolve blocking it isn’t the easiest/most efficient way. Keep in mind that creating a thread takes time and memory. Also, threads require the CPU to make context switching(switch from one thread to another) which takes time to perform.
CPU bound: Operations that will benefit from parallel execution. For example, let’s say you have an array of 20 numbers and you want to add 1 to each number. You can make two threads. Both will loop through the array and add 1 to the index that they are currently on. But thread 1 starts at index 0 and loops to index 9 meanwhile thread 2 goes from index 10 to index 19
Here is an analogy for better understanding the difference between the two. Imagine you have a shopping list of items. You will have to drive to a few different stores to complete your shopping list.
async/await:
- First, we go to the store at 8 A.M to buy some fruit. We manage to buy most of the fruit except oranges because they run out. A new delivery of oranges will arrive at 9 A.M. Instead of waiting we head to the next store to buy ourselves some new shoes.
- When we arrive at the shoe shop at 8:30 a.m. we find out that the store doesn’t open until 9.30 a.m. so we drive to the third store.
- At the clothes shop, we buy some new clothes and then head back to the first store to see if the oranges have arrived.
- We arrive at the first store at 9 o’clock and buy the oranges. Finally, we head back to the shoe shop. We buy some new shoes and then go home.
threads:
- We get 2 other people to help us with our shopping. Everyone goes to a different store at the same time and doesn’t return
- home until they get all the required items from that store.
Code:
using System; using System.Threading.Tasks; namespace AsyncAwaitAndTasks { class Program { static async Task Main(string[] args) { #region Await a task //Await the task without blocking the main thread. await doWork(); //We can also start multiple tasks. Task task1 = doWork(); Task task2 = doWork(); Tasktask3 = getDataFromDB(); //Do other stuff here while we wait for our tasks to be completed. Console.WriteLine("Doing other stuff."); //Wait for the completion of tasks. await task1; await task2; int result = await task3; //Continue with other code... Console.WriteLine("Tasks are done. Continue with other code."); //You can also call and await an async method in the same line. //But keep in mind that your code will not proceed from this line until a result is returned. int result2 = await getDataFromDB(); #endregion #region Await multiple tasks and get the returned values Task [] tasks = new Task [3]; //Create tasks. tasks[0] = getDataFromDB(); tasks[1] = getDataFromDB(); tasks[2] = getDataFromDB(); //Wait for all the tasks to finish. Task.WaitAll(tasks); //Get the data from the tasks. foreach (var item in tasks) Console.WriteLine(item.Result); #endregion Console.ReadLine(); } static async Task doWork() { //Simulate waiting on some resource like a DB, file, http call response, ... await Task.Delay(2000); } //This async method also returns a result. static async Task getDataFromDB() { //Simulate waiting on some resource like a DB, file, http call response, ... await Task.Delay(2000); //Let's pretend like we get this from a database... int value = 10; return value; } } }