• Articles
  • Tutorials
  • Interview Questions

Master C# Asynchronous Programming with Async/Await

Master C# Asynchronous Programming with Async/Await

Async programming allows your code to multitask, making your applications faster and more responsive. So, if you want to turbocharge your applications and keep your users happy, async programming in C# is your go-to tool! In this blog, we will discover C# Asynchronous programming, learn its basics, explore the asynchronous model, master async/await, and understand async and await functions.

Table of Contents

Watch the video below to understand the difference between C++ and C#

Video Thumbnail

What is C# Asynchronous Programming?

C# Asynchronous programming is a technique that allows a program to perform multiple tasks concurrently without waiting for each task to finish before moving on. In traditional synchronous programming, one task must be completed before another starts. This approach caused several delays. But with C# async, you can initiate tasks and move on to other work. This is crucial for responsive applications. It ensures the program doesn’t get stuck waiting for time-consuming operations. This ultimately makes your software more efficient and user-friendly.

Overview of the Asynchronous Model

The asynchronous model in programming is a technique that allows a computer to handle multiple tasks concurrently without blocking or waiting for each one to complete them before moving on to the next.

Asynchronous programming enables a program to initiate a task and then continue executing other tasks. It waits for the initiated task to be completed in the background. It’s like sending an email and being able to compose another message without waiting for the first one to be sent.

This approach is particularly useful in scenarios where tasks involve I/O operations, like file access or network communication. It ensures that the program remains responsive and doesn’t waste time idling. Asynchronous programming is a valuable tool for improving the efficiency and responsiveness of applications.

Get 100% Hike!

Master Most in Demand Skills Now!

Asynchronous Programming Patterns

Asynchronous Programming Patterns

Explore asynchronous programming patterns in C# to manage time-consuming tasks efficiently. Read the content below to understand how to implement Fire and Forget, OnFailure, Timeout, Retry, and Fallback techniques for improved coding.

Fire and Forget Pattern

Fire and forget is a pattern where you start a task and don’t wait for it to finish. This is useful when you don’t need the task’s result immediately and your main goal is to prevent your program from getting stuck while it does its work in the background. 

In the below example, we start the LongRunningOperation in the background, and the program continues without waiting for it to complete.

Example:

internal static class Program
{
    static async Task Main(string[] args)
    {
        Task.Run(async () =>
        {
            await LongRunningOperation();
        });
        Console.WriteLine("Operation started.");
        Console.ReadLine();
    }
    static async Task LongRunningOperation()
    {
        await Task.Delay(3000);
        throw new Exception();
    }
}

OnFailure Extension

OnFailure is an extension method used with tasks in C#. It allows you to specify an action to be executed if the task fails due to an exception. It’s handy when you expect a task to fail and want to take specific action when it does.

Example:

In this example, we use the OnFailure extension to handle exceptions when retrieving data from a URL.

internal static class Program

{
    static async Task Main(string[] args)
    {
        string url = "https://example.com/data";
        await RetrieveAsync(url).OnFailure(ex => Console.WriteLine($"Failed to retrieve data: {ex.Message}"));
        Console.WriteLine("hello");
    }
    public static async Task OnFailure(this Task task, Action<Exception> onFailure)
    {
        try
        {
            await task.ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            onFailure(ex);
        }
    }
    private static async Task<string> RetrieveAsync(string url)
    {
        await Task.Delay(3000).ConfigureAwait(false);
        throw new Exception("Failed to retrieve data from server");
    }
}

Timeout Mechanism

The timeout mechanism is used to limit the time a task can take to complete. It checks if the task finishes within a specified time and, if not, cancels the operation. In the below example, we set a timeout for data retrieval, and if it takes too long, the operation is canceled.

Example:

internal static class Program
{
    static async Task Main(string[] args)
    {
        string url = "https://example.com/data";
        try
        {
            await RetrieveAsync(url).WithTimeout(TimeSpan.FromSeconds(5));
            Console.WriteLine("Data retrieved successfully");
        }
        catch (TimeoutException)
        {
            Console.WriteLine("Data retrieval timed out");
        }
    }
    public static async Task WithTimeout(this Task task, TimeSpan timeout)
    {
        var delayTask = Task.Delay(timeout);
        var completedTask = await Task.WhenAny(task, delayTask);
        if (completedTask == delayTask)
        {
            throw new TimeoutException();
        }
        await task.ConfigureAwait(false);
    }
    private static async Task<string> RetrieveAsync(string url)
    {
        await Task.Delay(6000).ConfigureAwait(false);
        return "Data";
    }
}

Gain mastery in programming languages by enrolling in a Master’s in Computer Science today!

Retry Strategy

The retry strategy is used when you deal with unreliable or slow services. It attempts to execute an operation multiple times in case of failure, with delays in between, improving the chances of success.

Example:

In this example, we attempt to fetch data from a server with retries and delays in case of failure.

internal class Program
{
    static async Task Main(string[] args)
    {
        string url = "https://example.com/data";
        int maxRetries = 3;
        TimeSpan delayBetweenRetries = TimeSpan.FromSeconds(1);
        try
        {
            var data = await Retry(async () => await FetchDataFromServerAsync(url), maxRetries, delayBetweenRetries);
            Console.WriteLine($"Fetched data: {data}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Failed to fetch data after {maxRetries} attempts: {ex.Message}");
        }
    }
    public static async Task<TResult?> Retry<TResult>(Func<Task<TResult>> taskFactory, int maxRetries, TimeSpan delay)
    {
        for (var i = 0; i < maxRetries; i++)
        {
            try
            {
                return await taskFactory().ConfigureAwait(false);
            }
            catch
            {
                if (i == maxRetries - 1)
                {
                    throw;
                }
                await Task.Delay(delay).ConfigureAwait(false);
            }
        }
        return default;
    }
    private static async Task<string> FetchDataFromServerAsync(string url)
    {
        await Task.Delay(3000).ConfigureAwait(false);
        throw new NotImplementedException();
    }
}

Fallback Technique

The fallback technique provides a backup plan when the primary service fails. It’s particularly useful for ensuring continuity when dealing with external services. In the below example, we use the fallback technique to provide a fallback data value when the primary data retrieval fails.

Example:

internal static class Program
{
    static async Task Main(string[] args)
    {
        string url = "https://example.com/data";
        var data = await RetrieveAsync(url).Fallback("fallback data");
        Console.WriteLine($"Fetched data: {data}");
    }
    public static async Task<TResult> Fallback<TResult>(this Task<TResult> task, TResult fallbackValue)
    {
        try
        {
            return await task.ConfigureAwait(false);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Failed to retrieve data: {ex.Message}");
            return fallbackValue;
        }
    }
    private static async Task<string> RetrieveAsync(string url)
    {
        await Task.Delay(2000).ConfigureAwait(false);
        throw new Exception("Failed to retrieve data from server");
    }
}

Check out the Python programming Interview Questions to crack your interview!

Asynchronous Programming with Async and Await in C#

Asynchronous programming is a technique used to improve the responsiveness of software applications. It allows tasks to be executed concurrently, enabling efficient utilization of system resources and reducing the time a program spends waiting for specific operations to complete. In C#, this capability is facilitated by the async and await keywords.

Async Function in C#

An async function is a method in C# that is marked with the async keyword. This keyword indicates that the function contains asynchronous operations. When you invoke an async function, it initiates its execution and returns immediately, allowing the calling code to continue without waiting for the function to finish.

Inside an async function, you can use the await keyword to pause the function’s execution until a particular asynchronous operation completes. While the function is paused, the CPU is free to work on other tasks. When the awaited operation finishes, the async function resumes from where it was paused.

Here’s an example of an async function in C#:

async Task MyAsyncFunction()
{
    // Some synchronous code here
    // Asynchronous operation that is awaited
    await SomeAsyncOperation();
    // Code that runs after the asynchronous operation completes
}

Await Function in C#

The await keyword is used within an async function to indicate that the execution should pause until the awaited task completes. The await keyword can be used with any task that returns a result. The result of the awaited operation can be stored in a variable for further processing.

Here’s an example demonstrating the use of await:

async Task MyAsyncFunction()
{
    // Asynchronous operation that is awaited
    var result = await SomeAsyncOperation();
    // Code that runs after the asynchronous operation completes, using the 'result'
}

Enroll in our Python course today to take your programming skills to the next level!

Difference Between Async and Await

Async and await are key elements in C# asynchronous programming. async is used to define methods that can be paused and resumed, and await is used to pause and resume the execution of those methods, allowing them to work efficiently with asynchronous tasks.

Let us discuss their difference in detail-

AspectAsyncAwait
DefinitionAsync is a keyword used to mark a method as asynchronous, allowing it to pause and resume its execution, typically indicating that it contains asynchronous operations.Await is used within an async method to pause the execution of that method until an awaited asynchronous operation is completed. It is used to introduce asynchronicity.
RoleThe async keyword signifies that a method can house asynchronous operations and provides the capability for pausing and resuming execution.await is responsible for pausing and resuming the execution of an async method, allowing it to handle asynchronous tasks efficiently.
UsageThe async keyword is placed at the beginning of a method’s signature to indicate its asynchronous nature.await is used inside an async method, directly preceding an asynchronous operation that you want to pause the method for until it’s completed.

Example of C# Asynchronous Programming

Let’s say you want to build a weather app that fetches data from a remote server. To ensure your app remains responsive, you want to use asynchronous programming.

With C# asynchronous programming, you can send the request to the server and then continue with other tasks while waiting for the response. When the server responds, your app handles the data without blocking the entire process.

In the below example, await allows your app to stay responsive while fetching weather data asynchronously. This way, your app can juggle multiple tasks efficiently, providing a smoother user experience.

public async Task<string> GetWeatherAsync()
{
    // Send a request to the server without blocking the app
    var response = await WeatherApi.GetWeatherDataAsync();
    // Process the weather data when it's available
    return response;
}

Conclusion

Asynchronous programming in C# is a useful way to make your applications faster and more responsive. Time-consuming operations can be handled without interrupting the execution of your application by using the async and await keywords. This makes your apps easier to use because they won’t freeze or stop responding when they’re doing things like getting data from the internet, working with big files, or connecting to databases. Because asynchronous programming lets your software do more than one thing at once, it can multitask. 

Get your queries resolved on our Intellipaat’s .

FAQs 

What is the main benefit of using C# asynchronous programming?

The primary benefit of using C# asynchronous programming is improved responsiveness. It allows your program to continue running other tasks while waiting for time-consuming operations to be completed.

Do I need to use asynchronous programming for all tasks in my C# application?

No, asynchronous programming is best suited for tasks that may cause delays, like I/O operations (reading/writing files, network requests), so your application remains responsive.

Are there any performance trade-offs when using asynchronous programming?

Asynchronous programming can introduce some overhead due to task switching, but the overall gain in responsiveness often outweighs this.

Can I use async/await in any C# method?

You can use async and await in methods that return Task or Task<T>. They are not applicable to methods with void return types.

Is there a limit to how many asynchronous operations I can run concurrently?

While there’s no hard limit, you should be mindful of system resources. Overloading with too many concurrent tasks can lead to diminishing returns or even performance issues. It’s important to balance concurrency based on your application’s needs.

Course Schedule

Name Date Details
Python Course 14 Dec 2024(Sat-Sun) Weekend Batch View Details
21 Dec 2024(Sat-Sun) Weekend Batch
28 Dec 2024(Sat-Sun) Weekend Batch

About the Author

Senior Consultant Analytics & Data Science

Sahil Mattoo, a Senior Software Engineer at Eli Lilly and Company, is an accomplished professional with 14 years of experience in languages such as Java, Python, and JavaScript. Sahil has a strong foundation in system architecture, database management, and API integration.