async await and asynchronous programming in C#

Asynchronous Programming


Every application or software solves a problem statement and while doing so it requires doing some kind of I/O bound operations like reading or writing data from a file system, web service call to an API to get the data, or accessing data from a database or CPU bound operations such as doing complex calculations.


Suppose we have a program that does certain Tasks say (Task A, Task B, and Task C) which is done sequentially therefore Task B will only start when Task A is completed and Task C will be started once Task B is completed. This is inefficient because every task starts depending on the completion of another task although they are not dependent and also take a longer time to complete as well. Therefore, asynchronous programming came into the picture where we can execute the tasks in parallel hence completing work in less time and efficiently.

Let’s assume we have a UI application where we want to do some I/O operation like getting data from web service so the application will be blocked till we get the response from web services as the UI thread which initiated the call gets blocked but in case of asynchronous programming the call to web services will run in a separate thread which will not block UI thread, therefore, UI will remain responsive even if the call to web service is not completed. Gaming application has a similar requirement where we want the game to be responsive even if the application is doing other tasks which are achieved through asynchronous programming.

async and await keywords are the most simple and powerful way to achieve asynchronous programming in C# which we will discuss in this article.



Types of Asynchronous Programming in C#


Asynchronous Programming was first introduced in .Net Framework 2.0 which was called Asynchronous Programming Model (APM) and the following are the different patterns of Asynchronous Programming that exist in .Net Framework.


  1. Asynchronous Programming Model (APM): This pattern exposed long-running Framework API methods with pair of Begin and End Methods. ADO.Net has a APM counterpart methods like BeginExecuteReader() and EndExecuteReader().



  1. Event-based Asynchronous Pattern (EAP): This pattern approached the asynchronous model as an event-based model which starts an async method that will trigger a Completed event when the task is completed. For example, there was an asynchronous method called WebClient.DownloadStringAsync with an associated event WebClient.DownloadStringCompleted for downloading Webpage contents.


  1. Task asynchronous Programming Model (TAP) provides an abstraction over asynchronous code where we write code in the sequence of statements, just like always but the compiler does the actual transformation because some of the statements may start the work and return a Task that represents the ongoing work. The core of asynchronous programming is the Task and Task<T> objects which model asynchronous operations which are supported by async and await keywords.


Please note APM and EAP are legacy ways to implement asynchronous programming which was difficult and cumbersome to use so they are no longer recommended by Microsoft. Microsoft recommends TAP (Task-based Asynchronous Pattern) to implement asynchronous programming in our application.



Task keyword in C#


Microsoft recommends TPL to implement asynchronous programming and the core of TPL is Task and Task<T> which models asynchronous operations. For I/O-bound code, you can await an operation that returns a Task or Task<T>. Similarly, in CPU-bound code you can await an operation that is started on a background thread using Task.Run method.

The Task class represents a single operation that does not return a value and usually executes asynchronously. It performs the work which typically executes asynchronously on a thread obtained from the thread pool which doesn’t block the main application thread which initiated the call thereby increasing the responsiveness of the application.

It has Status property, as well as IsCanceled, IsCompleted, and IsFaulted properties to determine the state of the task.



public class Task : IAsyncResult, IDisposable



Task class is derived from System.Threading.Tasks namespace which implements IAsyncResult and IDisposable interface.


Async and Await Keyword


Async


the async modifier used in method makes it asynchronous method and following example defines an async method.

public async Task<bool> CheckPrimeNumber(int number)

{

    Task.Delay(10000);

    for (int i = 2; i < number; i++)

    {

        if (number % i == 0)

        {

            return false;

        }

    }

    return true;

}



Just adding an async modifier in a method will not give us any advantages of asynchronous programming unless await is also used somewhere in the method.

The Compiler will also give a warning as follows if we don’t use any await in the async method.

await_warning_c_sharp.png

Without using await in an async method it behaves synchronously.

Await


Applying await operator in any I/O-bound or CPU-bound operations doesn’t block the Main Thread of the application and processes the task in the background by obtaining the thread from the thread pool which makes the application responsive while the actual I/O-bound or CPU-bound operations continue till completion.

Every async method should have at least one await keyword to take advantage of asynchronous programming.

Examples


Let’s see an example that will show how asynchronous programming gives us performance benefits over synchronous programming.

In our example, we will download the string of a few popular websites using both synchronous as well as asynchronously and will check the total elapsed time to download the string in both cases.


Synchronous Method


long CalculateWebsiteTimeDownloadSync(string[] webSitesAddress)

{

    var stopwatch = System.Diagnostics.Stopwatch.StartNew();

    var httpClient = new HttpClient();

    Console.WriteLine($"Thread Id Sync Before Foreach: {System.Threading.Thread.CurrentThread.ManagedThreadId}");

    foreach (var website in webSitesAddress)

    {

        Console.WriteLine($"Thread Id Sync Foreach: {System.Threading.Thread.CurrentThread.ManagedThreadId}");

        var websiteString = httpClient.GetStringAsync(website).GetAwaiter().GetResult();

    }

    Console.WriteLine($"Thread Id Sync After Foreach: {System.Threading.Thread.CurrentThread.ManagedThreadId}");

    stopwatch.Stop();

    Console.WriteLine($"Sync Time: {stopwatch.ElapsedMilliseconds}");

    return stopwatch.ElapsedMilliseconds;

}



Asynchronous Method


async Task<long> CalculateWebsiteTimeDownloadAsync(string[] webSitesAddress)

{

    Console.ForegroundColor = ConsoleColor.Red;

    var stopwatch = System.Diagnostics.Stopwatch.StartNew();

    var httpClient = new HttpClient();

    var tasksList = new List<Task<string>>();

    Console.WriteLine($"Thread Id: {System.Threading.Thread.CurrentThread.ManagedThreadId}");

    await httpClient.GetStringAsync("https://codetosolutions.com");

    Console.WriteLine($"Thread Id After codetosolution: {System.Threading.Thread.CurrentThread.ManagedThreadId}");

    foreach (var website in webSitesAddress)

    {

        var websiteString = httpClient.GetStringAsync(website);

        Console.WriteLine($"Thread inside foreach Id: {System.Threading.Thread.CurrentThread.ManagedThreadId}");

        tasksList.Add(websiteString);

    }

    await Task.WhenAll(tasksList);

    Console.WriteLine($"Thread Id after WhenAll: {System.Threading.Thread.CurrentThread.ManagedThreadId}");

    stopwatch.Stop();

    Console.WriteLine($"Async Time: {stopwatch.ElapsedMilliseconds}");

    return stopwatch.ElapsedMilliseconds;

}



Output


async_await_asynchronous_method_c_sharp.png


In the above example, we can see that the Thread id remains constant in the Synchronous method but Thread Id in the Asynchronous method keeps changing after every asynchronous call and the Total elapsed time in the async method is 1.3 seconds compared to 5.8 seconds in the synchronous method which improves the performance of the application drastically.

Please notice in the asynchronous method we are not calling the GetStringAsync method sequentially instead we creating a task list for all the website calls and calling them concurrently using Task.WhenAll static method gives us additional performance benefits and also, creating the task list doesn’t change the thread id of the application as you might have noticed from the output.


Converting Synchronous to Asynchronous in C#



We might have noticed that the caller of the async method should also be asynchronous so eventually, all methods calling this newly asynchronous method must also become asynchronous and this pattern repeats itself up the call stack until it reaches the entry point e.g., event handlers.

It is often not desired to change all the methods to asynchronous just to call one newly asynchronous method therefore we can still call a synchronous method in an asynchronous manner using Task.Run method and take the advantage of asynchronous programming without doing any code change in the existing synchronous method.


Suppose we have a synchronous method like below which we want to call asynchronously.


public double[] GetFibonacciSeries(int length)

{

    double[] fibonacciSeries = new double[length];

    fibonacciSeries[0] = 0;

    fibonacciSeries[1] = 1;

    for (int i = 2; i < fibonacciSeries.Length; i++)

    {

        fibonacciSeries[i] = fibonacciSeries[i - 1] + fibonacciSeries[i - 2];

    }

    return fibonacciSeries;

}


public async Task<bool> PrintFibonacciSeries()

{

    Console.WriteLine("Enter the Length of Fibonacci Series to print");

    var fibLengthStr = Console.ReadLine();

    if (int.TryParse(fibLengthStr, out int fibLength))

    {

        //Calling Synchronous method Asynchronously using Task.Run

        var output = await  Task.Run(()=> GetFibonacciSeries(fibLength));

        Console.WriteLine($"Fibonnaci Series of Length {fibLength}: {string.Join(",", output)}");

    }

    else

    {

        Console.WriteLine("Please provide valid Input");

    }

    Console.ReadLine();

    return true;

}


Task.Run method is very useful in case we want to convert any synchronous call to asynchronous.


Advantages of async and await keyword


As we have noticed that using async and await modifier we get the performance benefit and the following are key benefits of the same

  • Using async and await increases the responsiveness of the application. E.g., In a desktop application when the compiler awaits an operation in an asynchronous manner, the application UI still responds to user actions like resizing, minimizing, etc. since the UI thread is returned is back. In synchronous applications, this will freeze the application and stop responding because the UI thread continues to be blocked till the operation completes.

  • It helps in application scaling in server-based applications because when await is encountered in an ASP.NET application, the thread is returned to the thread pool for other requests to use the thread. In the Synchronous method if the number of concurrent requests exceeds then all the threads are blocked due to the blocking operation hence application will throw HTTP Status code 503 service unavailable.

  • We get possible performance gains using concurrency and sometimes running the independent tasks in parallel in asynchronous programming.


Avoid using .Result and .Wait method in C#


Combining blocking waits such as .Wait or .Result with async/await in .Net Framework can results in deadlock because .Wait and .Result is a synchronization method that causes the calling thread to wait for the current task instance to complete unless the Task completes successfully or the task is canceled or throws an exception. Both the .Result and .Wait blocks the current thread until it completes therefore blocking the application to proceed further and causing deadlock.

Conclusion


async and await are powerful modifiers that don’t block the UI thread, making the application more responsive and increasing the application's performance because it releases the current thread to serve more incoming requests. It makes asynchronous programming very easy to implement in C# compared to APM and EAP patterns of Asynchronous programming.



Share This Post

Linkedin
Fb Share
Twitter Share
Reddit Share

Support Me

Buy Me A Coffee