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 for the video to be completely downloaded. Of course, we call it streaming media; just for this example, I am considering this as an asynchronous operation.
Another well-known example of the asynchronous operation is, the application window will allow interaction 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 to the Program. To simplify this, and instruct to run the code asynchronously; C# has introduced async
& await
keywords.
async
keyword instructs the compiler that the declared method has await
statements inside, and which has asynchronous operations to execute. async
method needs to have a 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 the compiler that, the function with this modifier has an await
statements inside; which has 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 the 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 access GetAwaiter()
method.
And also, older versions of C# are not allowed to use this modifier with Main
method. You will get the 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, execute synchronously. How it manages? 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 the async method; otherwise, the statements may not run asynchronously; even though you use both async
& await
keywords.
What exactly await
will do? That’s what we will discuss in the 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 the asynchronous method until the awaited Task completes. That means the statements after await
statement will not execute until the associated Task
completes its execution. But the program control can go 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 it carefully one more time, the above paragraph. The await
operator simply instructs to suspend the execution of the next statements, within the async
method, until the associated Task
completes. Once the Task
is completed, the asynchronous method resumes the execution where it was 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 manages this thread pool, which contains managed threads, to allow it to execute asynchronous operations.
Does it execute in an 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 operations.
The 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 return 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, the below statement is invalid; if SayHello() doesn’t return above mentioned types;
await SayHello();
The compiler will throw the 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 thread pool’s thread For example, the statement looks like below;
Task.Run( () => SomeFunction() );
From the above statement, SomeFunction() will execute in a separate thread, a thread in a thread pool. We can verify whether the Thread is the thread pool’s thread or not; by simply adding the below statement. It displays True
, when it is the thread pool’s thread; otherwise, it displays False
.
Console.WriteLine(System.Threading.Thread.CurrentThread.IsThreadPoolThread.ToString());
Maybe, I will write a separate article to discuss Task and Task<T> classes. In our sample program, we are using Task & Task<T> classes to run a function in an asynchronous way.
Complete working code
Let’s put it 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 it to execute the rest of the statements. It waits for, 5 seconds; before it executes the next statement after await
. This is one example of an asynchronous operation.
AnotherAsync method is an async
method we have developed, and it simply calls another method GetRandomNumber, and returns the result of the function; which is a newly generated random number. But, it requests to execute the 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 starts 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 simply displays a message.
The important thing, we need to observe here is; the way the functions are executed 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 cases, SayHello will execute ONLY when all the statements (which are mentioned before this method) are executed.
This has proved that our 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)
Yep, pretty much the same as the last 10 async/await articles I’ve read.