c sharp

How to Master Async Await in C# for Asynchronous Programming

In the modern development age, there are a lot of services and operations that the devices and applications need to carry out to provide the desired services. With the exponential growth and innovation that follows the modern CPUs, it has become easier and more necessary to build applications that can take advantage of the provided power to run multiple instructions in parallel.

Asynchronous programming, or async dev, allows an application to execute multiple operations without blocking the execution thread.

In this tutorial, we will learn about async programming in C# by understanding how to work with the “async” and “await” keywords.

Understanding the Async Await

In C#, we use the “async” and “await” keywords as modifiers. Let us explore how each keyword works.

Async modifies a method, a lambda expression, or any anonymous method to run asynchronously.

Depending on the implementation, the async methods return a Task<T> or void. It is good to avoid returning a void from an async method unless in the case of event handlers.

Next, we have the “await” operator. This operator is applied to a task in an “async” method to suspend the execution of the method until the task that we are awaiting is completed. As you can guess, the method that contains the “await” operator must run asynchronously or have the async modifier.

Basic Usage

To better understand how this method works, let us look at a basic implementation of the “async” and “await” modifiers.

Consider a basic example as shown in the following:

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("Starting");
        await PrintHelloWorldAsync();
        Console.WriteLine("Finished");
    }
    static async Task PrintHelloWorldAsync()
    {
        await Task.Delay(1000); // a task that takes long to complete
        Console.WriteLine("Hello, world!");
    }
}

The previous code provides an unequivocal and simple demonstration of using the “async” and “await” keywords for async programming.

We start by writing a basic string to the console. We then call the PrintHelloWorldAsync() method.

This method uses the “await” keyword to simulate an async task that takes a long time to complete. In this case, the task waits for one second. This can be any long-running task such as fetching the data from the network, API calls, etc.

Once we invoke the print method, we return the control to the “main” method which waits for the method to complete.

This should complete the task and print the string to the console. Finally, we return the control to the “main” method which prints the “Finished” string, indicating the program’s end.

Using “async” and “await”, we ensure that the main thread is free and not blocked during the task that is defined in the method.

Exception Handling

Like all development tasks, you are bound to encounter exceptions when executing the “async” tasks. It is, therefore, suitable to learn how to handle them if they occur in an “async” method.

Luckily, we can use basic try-and-catch blocks as we do when writing a synchronous code. An example demonstration is as follows:

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("Starting");
        await ProcessDataAsync();
        Console.WriteLine("Finished");
    }

    static async Task ProcessDataAsync()
    {
        try
        {
            await LoadDataAsync();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"An error occurred: {ex.Message}");
        }
    }

    static async Task LoadDataAsync()
    {
        await Task.Delay(1000); // long operation
        throw new Exception("Error loading data.");
    }
}

In this case, we perform the same operation by calling the ProcessDataAsync() method inside the “main” method. This, in turn, calls the LoadDataAsync() method which simulates a long-running operation. This throws an exception with an error message.

We then catch this exception in the catch block of the ProcessDataAsync method which returns the error message to the console.

Using the Task.WhenAll

C# also allows us to run multiple tasks in parallel and then await all of them using the “Task.WhenAll”.

Take a look at the following example code:

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("Starting");

        Task task1 = Task1();
        Task task2 = Task2();
        Task task3 = Task3();

        await Task.WhenAll(task1, task2, task3);

        Console.WriteLine("All Tasks Completed");
    }

    static async Task Task1()
    {
        await Task.Delay(1000);
        Console.WriteLine("Task 1 Completed");
    }

    static async Task Task2()
    {
        await Task.Delay(2000);
        Console.WriteLine("Task 2 Completed");
    }

    static async Task Task3()
    {
        await Task.Delay(1500);
        Console.WriteLine("Task 3 Completed");
    }
}

Let us examine the previous code in simple steps.

The first step is printing a basic message to denote a program execution.

We then define three main tasks and start their execution by their respective async method. The first task waits for 1 second, the second for two, and the third waits for 1.5. Each task prints a message to the console upon completion.

Note: We use the Task.WhenAll() method to await the completion of all three tasks before proceeding. This ensures that the program does not terminate prematurely until all the tasks are completed.

Dealing with Deadlocks

Like any case of async programming, it is good to learn how to handle the deadlocks in the program. C# allows us to use the ConfigureAwait(false) method to avoid the deadlocks when we don’t need the context to resume on the original context.

A basic snippet is as follows:

static async Task<string> GetDataAsync()
{
    await Task.Delay(1000);
    return "Data";
}
static async Task ProcessDataAsync()
{
    string data = await GetDataAsync().ConfigureAwait(false);
    Console.WriteLine(data);
}

Conclusion

This tutorial explored the most essential part of writing the async programs using the C# “async” and “await” keywords. You can reference the documentation for a more complex implementation and best practices.

About the author

John Otieno

My name is John and am a fellow geek like you. I am passionate about all things computers from Hardware, Operating systems to Programming. My dream is to share my knowledge with the world and help out fellow geeks. Follow my content by subscribing to LinuxHint mailing list