C# – Asynchronous programming – async and await

Usually when we run the C# code, each statement executes synchronously; that means, one after another statement, executes sequentially. But, how do we instruct to run the code asynchronously?. For example, watching a video while it is downloading; instead of waiting the video to completely downloaded. Of course, we call it as streaming media; just for this example, I am considering this as an asynchronous operation.

Another well known example of asynchronous operation is, application window will allow to interact even though some background operations are in progress.

We use threads for asynchronous operations. Creating and Managing the Threads require more code to be added into the Program. To simplify this, and instruct to run the code asynchronously; C# has introduced async & await keywords.

async keyword instructs to the compiler that the declared method has await statements inside, and which has asynchronous operations to execute. async method needs to have an await statement to instruct to run the code asynchronously. Otherwise, the statements within async method will execute synchronously.

You need to understand one important thing here is, when we use these keywords, we are instructing to execute the statements in async method asynchronously. The statements may or may not execute asynchronously; this is purely depending on the way you use statements inside an async method.

There are different ways we can achieve asynchronous execution of code. Through this Article, we are going to discuss it with the help of async, await keywords & Task class.

async modifier

async modifier instructs to the compiler that, the function with this modifier has an await statements inside; which has an asynchronous operations to execute. We can use this modifier with lambda expressions and methods (both named and anonymous methods).

Can we use this async keyword with all the methods? No. This modifier can be used ONLY with the methods; whose return type is Task, Task<T> or void. Otherwise, you will see below Error message, when you build your C# program.

Error 1 The return type of an async method must be void, Task or Task<T>

From C# 7.0; async methods can return any type that has an accessible GetAwaiter() method.


And also, older versions of C# are not allowed to use this modifier with Main method. You will get below error message, when you attempt to use async modifier with Main method; or an entry point.

Error 1 'Async_Await_Demo.Program.Main(string[])': an entry point cannot be marked with the 'async' modifier

Within the asynchronous methods, usually the statements will run asynchronously. By mentioning await statement in an aysnc method; we are instructing to run the statements asynchronously. Otherwise, the statements within async method, executes synchronously. How it manage? This is completely behind the scene of async & await keywords. And as I mentioned above, you need to be cautious when you use statements within async method; otherwise, the statements may not run asynchronously; eventhough you use both async & await keywords.

What exactly await will do? That’s we will discuss in below section.

If you write the asynchronous method, without await statements within it; the compiler will throw the below Warning message.

Warning 5 This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

await keyword

The await keyword inserts the suspension point in the execution of asynchronous method until the awaited Task completes. That means, the statements after await statement will not execute until the associated Task completes it’s execution. But the program control can goes back to the function from where this asynchronous method is called; which allows the execution of the next statements in the called function. This is possible when the associated Task will execute in asynchronous mode.

Doesn’t it clear? Read carefully one more time, the above paragraph. The await operator simply instructs to suspends the execution of next statements, within the async method, until the associated Task completes. Once the Task is completed, the asynchronous method resumes the execution where it suspended; that means, the statements after await statement will continue to execute.

Does it mean, async method will run in a separate thread? Not necessarily. That is the beauty of this feature. Associated Task will execute in the current thread or in a separate thread. A separate thread, doesn’t mean a new thread; it is a thread from Thread Pool. .Net framework manage this thread pool, contains managed threads, to allows to execute asynchronous operations.

Does it executes in asynchronous way, every time when we use Tasks? Not really. The tasks may or may not run asynchronously. This is completely depending on the way you write the program code. Through this article, we are going to achieve this by using Task’s Run() & Delay() methods. Task classes are useful for asynchronous operation.

Important thing you need to remember is, you can’t use await keyword with all the statements; you can use this, only with the statements which returns Task or Task<T> type or void types (this is for Event handlers). From C# 7.0, we can use these with any type that has an accessible GetAwaiter() method. 

That said, below statement is invalid; if SayHello() doesn’t return above mentioned types;

await SayHello();

The compiler will throw below Error message;

Error 1 Cannot await 'void'

Task and Task<T> are the classes defined in System.Threading.Tasks namespace, useful for asynchronous operations. It has a Run() method, through which we can instruct the portion of the code or function to run in a separate thread; a threapool’s thread For example, the statement looks like below;

Task.Run( () => SomeFunction() );

From above statement, SomeFunction() will execute in a separate thread, a thread in a thread pool. We can verify whether the Thread is threadpool’s thread or not; by simply adding the below statement. It displays True, when it is threadpool’s thread; otherwise, it display False.

Console.WriteLine(System.Threading.Thread.CurrentThread.IsThreadPoolThread.ToString());

May be, I will write a separate article to discuss on Task and Task<T> classes. In our sample program, we are using Task & Task<T> classes to run a function in asynchronous way.

Complete working code

Let’s put all together and our code looks like below. This code was tested in Windows 7 Operating System and developed using Visual Studio 2012 IDE.

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace Async_Await_Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Async demo, one example with Task.Delay() function
            Console.WriteLine("====== Async demo ======");
            Task task1 = SomeAsync();

            SayHello();

            Console.WriteLine("Waiting... async function to return.");
            task1.Wait();            

            // Async demo, another example with Task.Run() function
            Console.WriteLine("\n\n====== Async demo - usually run in a separate thread ======");
            Task task2 = AnotherAsync();

            SayHello();

            Console.WriteLine("Waiting... async function to return.");

            Console.WriteLine(">>> Random Number: " + task2.Result);
           
            task2.Wait();
        }

        static async Task SomeAsync()
        {
            Random r = new Random();
            
            await Task.Delay(5000);

            Console.WriteLine(">>> Random number: " + r.Next());            
        }      

        static async Task AnotherAsync()
        {
            // Instruct to run some action in a thread pool's Thread.
            int result = await Task.Run(() => GetRandomNumber());

            return result;
        }

        static int GetRandomNumber()
        {            
            Random r = new Random();

            for (UInt32 i = 0; i < System.UInt32.MaxValue; i++);
            
            return r.Next();
        }

        static void SayHello()
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

Code walk-through

Now, this is the time to walk-through the code. It has two async methods; SomeAsync & AnotherAsync. And it has two normal functions; SayHello & GetRandomNumber.

SomeAysnc is an async method, which displays the new random number. But, before it displays the random number; it simply delays the execution of the function for 5 seconds, and gives back the control to the called function to allow to execute rest of the statements. It waits for, 5 seconds; before it executes next statements after await. This is one example of asynchronous operation.

AnotherAsync method is an async method we have developed, and it simply calls another method GetRandomNumber and it returns the result of the function; which is newly generated random number. But, it requests to execute GetRandomNumber function, in a thread. And await statement will suspend the execution of the async method until the thread completes execution. Meanwhile, the control goes back to the called function (in this case, it is Main); and start executing the statements mentioned after the async method.

GetRandomNumber simply returns the random number. I just keep it busy, by putting a long loop to give the feeling that some lengthy operation is in-progress. 🙂

SayHello is simply displays a message.

The important thing, we need to observe here is; the way the functions are executing and the results were shown. We called SayHello after the async methods. As the async methods executed asynchronously, you will see the Hello message before the completion of async methods; in normal case, SayHello will execute ONLY when all the statements (which are mentioned before this method) are executed.

This has been proved that, out program is executing asynchronously.

Another important thing is, we have called Wait() methods of the Task; to keep the Main method to wait until the completion of the async methods. This is important; because the chances are there, the Program terminates before the completion of async methods.

(Raju)

One Comment

Add a Comment

Your email address will not be published.