Asynchronous Functions in C# - Async and Await

Discussion in 'C#' started by shabbir, Apr 24, 2014.

  1. shabbir

    shabbir Administrator Staff Member

    Joined:
    Jul 12, 2004
    Messages:
    15,375
    Likes Received:
    388
    Trophy Points:
    83
    In my last tutorial on basics of threading, I explained what threading basically is and how it is actually implemented in C#. I would advise you to have a look at it before continuing with this article or if you have an Idea of what threads actually are, you can continue with this article. Writing a function and implementing a piece of code in it is not a big deal, anyone can write a function, pass it two variables add them and return back to the calling function. But in real world scenarios, where multiple tasks need to be performed simultaneously, this approach lacks sophistication and efficiency. This is why threading is so important because of threading comes with its ability to allow developers to write concurrent function which is more advanced form of functions which foster concurrency,

    Difference between Synchronous & Asynchronous Functions



    Before, dwelling into details of asynchronous functions, let us first differentiate between the synchronous and asynchronous functions. In simplest words, synchronous functions block the calling function until they complete their task. For instance, Thread.Sleep, Console.WriteLine and Console.ReadLine, these function do not let the control to shift to the next lines until they perform their functionality. Asynchronous methods are those methods which return immediately to the caller function and continue executing their task in parallel with the caller function. They do not block the caller function and it is due to this reason that they are called non-blocking functions.

    Asynchronous Programming



    Traditionally concurrency is implemented via creating a function outside the body of caller’s function and then passing this function as a delegate to a thread or task in order to achieve concurrency. Concurrency can be achieved this way but it comes with some overheads. In contrast to implementing concurrency via threads where function that has to be run parallel was initiated from outside of the calling function, asynchronous functions initiate concurrency from within the function instead of handing this responsibility over to threads or tasks.

    Course-grained concurrency vs. Fine-grained Concurrency



    Suppose there is a method that calculates the sum of squares of the range of numbers passed to it and then displays it. Calculating sum of squares of range of number can be a long task. Therefore, it can be divided into two functions. One function that calculates the sum of squares of range of numbers and the other function that displays this result. Still, major portion of time would be spent on calculating sums rather than displaying. Traditionally in order to display the sums concurrently, we create a new thread or task and pass a delegate to a method that displays the sum. This method would in turn call the method that calculates the actual sum of range of variable. This is the concept of course-grained concurrency. In our first example, we explain this concept.

    Example1

    Code:
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace CSharpTutorial
    {
        class Program
        {
            public static void DisplaySumOfSquaresOfRange()
            {
                int i = 1;
                while (i <= 1000)
                {
                    Console.Write("\nSum of squares of numbers between "+ i + " and " + (i + 9) + " = "); 
                    Console.Write(GetSumOfSquaresOfRange(i, i + 9));
                    i = i + 10;
                }
            }
    
            public static int GetSumOfSquaresOfRange(int num1, int num2)
            {
                int result = 0;
    
                for (int i = num1; i <= num2; i++)
                {
                    result = result + (i * i);
                }
                return result;
            }
            public static void Main()
            {
    
              Task newtask =  Task.Run(()=>DisplaySumOfSquaresOfRange());
                Console.ReadLine();
            }
        }
    }
    
    In Example1, there are two static methods: GetSumOfSquaresOfRange which actually calculates the sum of squares of numbers between the two variables passed to it. DisplaySumOfSquareOfRange is the method which is used to display the sum of squares of all the numbers between 1 and 1000. The range is calculated and displayed in groups of 10. For instance, first the method would display the sum of squares of all the integers between 1 and 10, then 11 and 20 and so on up to thousand.

    In the main method, we have created a newtask and have passed it the DisplaySumOfSquaresOfRange method which would in turn call GetSumOfSquaresOfRange method. These chains of methods are called graphs programming terms. Both of these two functions will run on a new task or pooled thread. But main functionality is only being perfomed by GetSumOfSquaresOfRange method. This is called course-grained concurrency where all the chained methods are on a separate thread but most of them do not perform long tasks. In rich client applications such as WPF and Windows Forms, the course-grained concurrency can result in less responsive UI since there are multiple functions chained on one thread or task. The output of the code in Example1 is as follows:

    Output1

    [​IMG]

    Note: The output is huge; therefore we have printed some portion of it to demonstrate the concept.

    In console applications, course-grained concurrency doesn’t have much impact on the performance since user interaction and need for responsiveness is minimal. However, in rich client application, course-grained concurrency can hinder the performance and responsiveness of the UI. In such cases, fine-grained concurrency is a more efficient solution. In the next example of the tutorial, Example1 has been modified to demonstrate how fine-grained concurrency is implemented in applications. Have a look at the second example of this tutorial.

    Example2

    Code:
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace CSharpTutorial
    {
        class Program
        {
            public static async Task DisplaySumOfSquaresOfRange()
            {
                int i = 1;
                while (i <= 1000)
                {
                    Console.Write("\nSum of squares of numbers between " + i + " and " + (i + 9) + " = " + await GetSumOfSquaresOfRange(i, i + 9));  
                    i = i + 10;
                }
            }
    
            public static Task<int> GetSumOfSquaresOfRange(int num1, int num2)
            {
                 return Task.Run(() =>
                    {
                        int result = 0;
                        for (int i = num1; i <= num2; i++)
                        {
                            result = result + (i * i);
                        }
                        return result;
                    }    
                );
            }
    
            public static void Main()
            {
                DisplaySumOfSquaresOfRange();
                Console.ReadLine();
            }
        }
    }
    
    In Example2, in the main method it can be seen that DisplaySumOfSquaresOfRange has been called directly without passing it in any thread or task. However, in order to make DisplaySumOfSquaresOfRange and GetSumOfSquaresOfRange run asynchronously, certain modifications are required to be made.

    The first modification is made in all those functions that take long time. In Example2, GetSumOfSquaresOfRange takes longer time for execution, therefore we have to append keyword Task, followed by the type of data which this method returns, Task<int> in case of GetSumOfSquaresOfRange. Inside the method, everything has to be enclosed inside the Task.Run method. This is called fine-grained concurrency because not all the methods on the chain will run in a separate thread but only those which take longer time. This makes rich client application more responsive and efficient.

    The second modification is required to be made in all those functions which call this GetSumOfSquaresOfRange method. Calling functions should be made async by appending async keyword before the method declaration as we did for the DisplaySumOfSquaresOfRange method which calls GetSumOfSquaresOfRange method. We did this in function declaration as follows:
    Code:
    public static async Task DisplaySumOfSquaresOfRange()
    
    The next modification is made in the call to the method GetSumOfSquaresOfRange. The keyword “await” has to be appended before the call. In DisplaySumOfSquaresOfRange method, we did this in the Console.Write method where we write this:
    Code:
    await GetSumOfSquaresOfRange(i, i + 9).
    
    The output of the code in Example2 will be exactly identical to that of Example1.

    Understanding ‘await’ and ‘async’ keywords



    You saw that we used ‘await’ and ‘async’ keywords for fine-grained concurrency, let see what these functions actually does. If you have read the article on ‘Understanding threads and tasks’ you would remember that we used the following expression in the 8th Example of that tutorial:
    Code:
                    Task<int> nt = Task.Run(() => Division(15, 3));
    
                    var divisionawaiter = nt.GetAwaiter();
    
                    divisionawaiter.OnCompleted(() => 
                        {
                            int division = divisionawaiter.GetResult();
                            Console.WriteLine("Displaying division after awaiter completes: " + division);
                        });
                    Console.WriteLine("This will execute without awaiting ....");
    
    Here when Division method completed its execution, the OnComplete method of the divisionwaiter executed to which we could pass any expression.

    The new “await” and “async keywords do exactly the same job but in a more cleaner and efficient. Though, we have implemented ‘await’ and ‘async’ keywords in Example2, but in our next example, we would perform Division as we did in the above code snippet, but this time, using ‘await’ and ‘async’. Have a look at the code 3rd Example:

    Example3

    Code:
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace CSharpTutorial
    {
        class Program
        {
         public static async Task DisplayDivision(int num1, int num2)
         {
             var result = await Division(num1, num2);
             Console.WriteLine("Displaying division after awaiter completes: " + result);
         }
    
            public static Task<int> Division(int num, int num2)
            {
                return Task.Run(() =>
                    {
                        int result = num / num2;
                        return result;
                    }
                );
            }
            public static void Main()
            {
                DisplayDivision(15, 3);
                Console.WriteLine("This will execute without awaiting ....");
                Console.ReadLine();
            }
        }
    }
    
    Compare the code in Example8 of my tutorial on threading & tasks with the code in Example3 of this tutorial. Here, we did not directly call await because to call await, the calling method must be specified ‘async’ and the return type of the method should be Task. We cannot make a Main method ‘async’ because it is the entry point for the code. For this purpose, we have written an intermediate method DisplayDivision that displays the result of the Division. Notice the following lines of code in DisplayDivision method of Example3:
    Code:
    var result = await Division(num1, num2);
    Console.WriteLine("Displaying division after awaiter completes: " + result);
    
    These lines are equal to following lines:
    Code:
                    Task<int> nt = Task.Run(() => Division(15, 3));
    
                    var divisionawaiter = nt.GetAwaiter();
    
                    divisionawaiter.OnCompleted(() => 
                        {
                            int division = divisionawaiter.GetResult();
                            Console.WriteLine("Displaying division after awaiter completes: " + division);
                        });
                    Console.WriteLine("This will execute without awaiting ....");
    
    Note that GetAwaiter has been totally eliminated and OnCompleted method is also not required to be called.
    Code:
    var result = await Division(num1, num2);
    
    In the above lines, behind the scene GetAwaiter is called on the Task of Division method and when Division method completes execution, OnCompleted is called and the value returned by the Division method is stored in result variable which can be displayed any time. But bear in mind that you can only call ‘await’ on those methods whose return type is Task<type> and they return a task like in Division method of Example3.

    Secondly, ‘await’ can only be called inside those methods that have been specified ‘async’ as DisplayDivision method in Example3. The output of the code in Example3 would be as follows:

    Output3

    [​IMG]

    The methods which have ‘async’ modifier associated with them are typically called asynchronous methods. It is due to the fact that when a compiler reaches ‘await’ keyword, what happens is that control is shifted back to the method in which ‘await’ is present but before giving control back to the calling method, a continuation is attached with the task (await is always on a task). When task completes, the control is again shifted back to the continuation which is executed.

    Best thing about keyword ‘await’ is that it can be used anywhere inside the method which has modified as ‘async’. It can be used inside loops, with lambda expressions, delegates or anywhere you want except for the catch, finally, unsafe and lock blocks.

    Making function both asynchronous and awaitable



    A very good approach is to make async methods, themselves awaitable. In fact all the methods in a method chain should be made awaitable except the method which is called from the main method. To make a method both asynchronous and awaitable, you have to use async modifier and the next change is the return type of the method which should be Task. The new method can be both awaited also, ‘await’ can be used inside the method. The 4th example of this tutorial demonstrates this concept.

    Example4

    Code:
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace CSharpTutorial
    {
        class Program
        {
         public static async void StartAsynchrony()
         {
             await Task.Delay(TimeSpan.FromSeconds(3));
             Console.WriteLine("Starting chain");
             await FirstMethod();
             Console.WriteLine("Method 1 has been called");
         }
    
         public static async Task FirstMethod()
         {
             await Task.Delay(TimeSpan.FromSeconds(3));
             Console.WriteLine("This is Method 1");
             await SecondMethod();
             Console.WriteLine("Method 2 has been called");
    
         }
    
         public static async Task SecondMethod()
         {
             await Task.Delay(TimeSpan.FromSeconds(3));
             Console.WriteLine("This is Method 2 ");
    
         }
            public static void Main()
            {
                StartAsynchrony();
                Console.WriteLine("This will execute without awaiting ....");
                Console.ReadLine();
            }
        }
    }
    
    Here in the above example, StartAsynchrony method is only asynchronous because it has been modified with ‘async’ keyword. You can use ‘await’ keyword inside this method but this method is not awaitable and you cannot call ‘await’ on this method for instance you can’t call it as:
    Code:
    await StartAsynchrony()
    
    On the other hand, FirstMethod and SecondMethod are both asynchronous as well as awaitable because they have been modified with keyword ‘async’ also their return type is ‘Task’. You can call await both INSIDE these methods and also ON these methods as it has been done in Example4 of this article. The output of the code in Example4 is as follows:

    Output4

    [​IMG]
     
    Last edited: Jan 21, 2017

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice