In C#, async and await are keywords that simplify the syntax when working with asynchronous tasks, also making it easier to read.
- async: Placed before a method to indicate it contains asynchronous operations.
- await: Pauses execution until the asynchronous task completes and allows the thread to be released to execute other tasks in the meantime.
How do async and await work?
When a method is marked as async, it means this method does not block the thread that executes it (i.e., it runs asynchronously).
On the other hand, an async method can contain one or more operations marked with await. This means the method will “wait” for this operation to finish before continuing.
Once the “awaited” operation completes, the async method resumes execution from where it left off.
It’s important to remember that:
asyncmethods must returnTask,Task<T>, orvoid.awaitcan only be used insideasyncmethods.
A common mistake is thinking you have to await all async methods. It’s perfectly valid to want to launch an async method and have it run in parallel.
Async and await example
Let’s see it with an example. Suppose we have a VeryLongOperation that we want to execute in a non-blocking way.
First, we’ll mark the method as asynchronous using the async keyword.
// Asynchronous method simulating a long-running operation
static async Task VeryLongOperation()
{
Console.WriteLine("Start of long-running operation...");
await Task.Delay(3000); // Simulate a 3-second process
// Rest of the code
Console.WriteLine("Operation completed.");
}
This means that when we invoke this function, it will execute in a non-blocking way (in parallel).
Now suppose we want to call that function. We could do it like this.
static async Task Main()
{
Console.WriteLine("Start of Main...");
// Call an asynchronous method
await VeryLongOperation();
Console.WriteLine("End of main.");
}
If we run this, we will get the result
Start of Main… Start of long-running operation…
(a 3s pause).
Operation completed. End of main.
That is,
- The
Mainfunction started and calledVeryLongOperation Mainwaited forVeryLongOperationto finishMainfinished
The “chain” of asyncs
A curious thing about async and await is that they “chain”. The method you mark as async has to be invoked by an async, which has to be invoked by…
That is, once you start using async methods, everything that uses them will have to be async (all the way up to the main Main method). At some point, you will have to “break the chain”.
To do this, remember you can use Task.Wait();. For example, this version of the previous Main is not asynchronous.
static void SynchronousMain()
{
Console.WriteLine("Start of Main...");
VeryLongOperation.Wait();
Console.WriteLine("End of main.");
}
Combining multiple asynchronous tasks
In some cases, it is necessary to run multiple tasks asynchronously. This can be done using Task.WhenAll or Task.WhenAny.
- Task.WhenAll: Runs multiple tasks concurrently and waits until all are complete.
- Task.WhenAny: Runs multiple tasks and returns when any of them complete.
static async Task RunTasksConcurrently()
{
Task task1 = Task.Delay(2000); // Simulates a 2-second task
Task task2 = Task.Delay(3000); // Simulates a 3-second task
// Waits until both tasks are complete
await Task.WhenAll(task1, task2);
Console.WriteLine("All tasks have completed.");
}
