Asynchronous C# programming using async and await keywords

USING ASYNC / AWAIT KEYWORDS IN C#

by

Asynchronous programming is a technique that allows you to write code that can perform multiple tasks concurrently without blocking the main thread. This can improve the responsiveness and performance of your applications, especially when they involve I/O-bound operations such as web access, file access, or database access.

In this blog post, you will learn how to use the async and await keywords in C# to write asynchronous methods that can leverage the Task-based Asynchronous Pattern (TAP) supported by the .NET runtime. You will also learn the basics of how async and await work behind the scenes and what are some best practices to follow when working with asynchronous methods.

What are async and await keywords?

The async and await keywords in C# are the heart of async programming. By using those two keywords, you can use resources .NET Core to create an asynchronous method almost as easily as you create a synchronous method.

The async keyword is used to mark a method as asynchronous. This means that the method can contain one or more await expressions, which indicate that the method should pause and wait for an asynchronous operation to complete before resuming. The async keyword also enables the compiler to generate the necessary code to handle the asynchronous logic of the method.

The await keyword is used to apply the await operator to an expression that represents an asynchronous operation. The await operator tells the compiler to suspend the execution of the async method until the awaited task is complete. In the meantime, control is returned to the caller of the async method, which can continue with other work that doesn't depend on the result of the async method. When the awaited task is complete, the async method resumes where it left off.

How to write an async method

The following steps will help you write async methods:

  1. Add the async modifier to the method signature. The method can have any name, but it's recommended to use a suffix of "Async" for consistency and clarity, for example 'DoSomethingAsync()'
  2. Choose a return type for the method. The return type can be one of these three options:
    • void, if the method doesn't return any value and is only used for event handlers.
    • Task, if the method returns no value but needs to report completion or exceptions to its caller.
    • Task<T>, if the method returns a value of type T.
  3. Use the await operator inside the method body to call other async methods or APIs that return tasks. You can use await multiple times in an async method, as long as each awaited expression is independent from each other. You can also use other control flow constructs such as if, for, while, try/catch/finally with await, as long as you don't block or wait synchronously inside an async method.
  4. If your method returns a value, use the return statement as usual. If your method doesn't return a value, use return; or omit it entirely.
  5. If your method needs to handle exceptions, use try/catch/finally blocks as usual. Any exception thrown by an awaited task will be rethrown when you await it, so you can catch it at that point. You can also use Task.IsFaulted, Task.Exception, or Task.Wait() methods to inspect or handle exceptions from tasks.

How to call an async method

When calling an async method, keep in mind the following steps:

  1. Use the await operator inside another async method to call an async method and wait for its result. This way, you can write code that looks like synchronous code but actually runs asynchronously.
  2. If you don't want to wait for the result of an async method, you can assign its task to a variable and use it later. For example, you can use Task.WhenAll() or Task.WhenAny() methods to combine multiple tasks and await them together.
  3. If you need to call an async method from a non-async context, such as a console application's Main() method or a UI event handler, you can use one of these options:
    • Use .GetAwaiter().GetResult() or .Result properties on the task returned by the async method. This will block the current thread until the task is complete and return its result or throw its exception. This option should be used with caution, as it can cause deadlocks or performance issues if used improperly.
    • Use .ConfigureAwait(false) on every await expression inside your async methods. This will prevent your async methods from capturing and resuming on the original synchronization context, which may not be available in non-async contexts. This option can improve performance and avoid deadlocks, but it may also change the behavior of your code if you rely on the synchronization context for certain operations.

Consider the following example for writing and calling async methods.

// This method is async because it has the async modifier in its signature
// It returns a Task because it doesn't return any value but needs to report completion or exceptions to its caller
public async Task SumAsync(int a, int b)
{
    // This method uses the await operator to call another async method that returns an int    
    int c = await AddAsync(a, b);
    // Do something with c
    // This method doesn't use the return statement because it doesn't return any value
}

// This method is also async because it has the async modifier in its signature
// It returns a Task<int> because it returns a value of type int
public async Task<int> AddAsync(int x, int y)
{
    // Simulate a long-running operation that takes 1 second to complete
    await Task.Delay(1000);
    return x + y;    
}

The Bottom Line

In this tutorial, you learned how to use the async and await keywords in C# to write asynchronous methods that can perform multiple tasks concurrently without blocking the main thread. You have also how async and await work behind the scenes and what are some best practices to follow when using them.


Don't stop learning!

There is so much to discover about C#. That's why I am making my favorite tips and tricks available for free. Enter your email address below to become a better .NET developer.


Did you know?

Our beautiful, multi-column C# reference guides contain more than 150 tips and examples to make it even easier to write better code.

Get your cheat sheets