MSDN Documentation

Asynchronous Programming with async and await in C#

Asynchronous programming allows your application to perform long-running operations without blocking the user interface or the main execution thread. This leads to a more responsive and efficient application. In C#, the async and await keywords simplify the process of writing asynchronous code.

What is Asynchronous Programming?

Traditionally, when a method needs to perform a time-consuming task (like downloading a file, querying a database, or performing complex calculations), it blocks the thread it's running on until the task is complete. This can result in applications that appear "frozen" to the user. Asynchronous programming enables these operations to run in the background, freeing up the current thread to handle other tasks.

The async Modifier

The async modifier indicates that a method, lambda expression, or anonymous method is an asynchronous method. An asynchronous method is a method that can be suspended and then resumed later. The compiler translates the async method into a state machine that manages the execution flow.

A method marked with async must adhere to the following rules:

The await Operator

The await operator is applied to an awaitable type (typically a Task or Task<TResult>). When the execution reaches an await expression:

  1. The execution of the asynchronous method is suspended.
  2. Control is returned to the caller of the asynchronous method.
  3. The awaited operation continues to run in the background.
  4. When the awaited operation completes, execution resumes from that point in the asynchronous method.

If the awaited task is a Task<TResult>, the await operator unwraps the result of the task. If the awaited task throws an exception, that exception is rethrown when execution resumes.

Example: Downloading a Web Page Asynchronously

Consider downloading the content of a web page. A synchronous approach would block the thread. Here's how you'd do it asynchronously:


using System;
using System.Net.Http;
using System.Threading.Tasks;

public class WebDownloader
{
    public static async Task<string> DownloadPageAsync(string url)
    {
        using (HttpClient client = new HttpClient())
        {
            Console.WriteLine($"Starting download from {url}...");
            // The await suspends execution here until the download is complete
            string content = await client.GetStringAsync(url);
            Console.WriteLine($"Download complete. {content.Length} characters received.");
            return content;
        }
    }

    public static async Task Main(string[] args)
    {
        string url = "https://www.example.com";
        // Calling the async method. Execution returns here immediately after await client.GetStringAsync() is hit.
        string pageContent = await DownloadPageAsync(url);
        Console.WriteLine("Program finished.");
        // You can now use pageContent
    }
}
        

Explanation:

Tip: Always use Task.WhenAll when you want to execute multiple asynchronous operations concurrently and wait for all of them to complete.

Returning void from an async Method

While generally discouraged, async void methods are primarily used for event handlers. This is because event handlers in .NET typically have a void return type. However, there's a significant drawback: you cannot await an async void method. If an exception occurs in an async void method, it will likely crash your application because there's no mechanism to catch those exceptions from outside the method.


// Example of an async void event handler (use with caution)
private async void Button_Click(object sender, EventArgs e)
{
    try
    {
        string result = await SomeLongRunningOperationAsync();
        MessageBox.Show(result);
    }
    catch (Exception ex)
    {
        // Exception handling is crucial here, but difficult to manage externally.
        MessageBox.Show($"An error occurred: {ex.Message}");
    }
}
        
Warning: Avoid using async void for general-purpose asynchronous methods. Prefer Task or Task<TResult>.

Best Practices

Note: The async and await keywords are part of C# 5.0 and later. Ensure your project targets a compatible .NET version.

Further Reading