Introduction to Asynchronous Programming in C#
Asynchronous programming is one of the key concepts of modern C# development. In the era of high-load web applications, microservices, and cloud solutions, the ability to effectively work with asynchrony has become a mandatory skill for every .NET developer.
The main goal of asynchronous programming is not to block the execution thread while waiting for long-running operations (I/O, network operations, database queries). Instead, the thread is freed to handle other tasks, which significantly improves application performance and scalability.
In this article, we will cover:
- Basics of async/await in C#
- Working with the Task class
- Rules for writing asynchronous methods
- Common mistakes and how to avoid them
Basics of async/await in C#
The key paradigm of asynchronous programming in C# is built on two keywords: async and await. They were introduced in C# 5.0 and have since become the standard for working with asynchrony.
How async/await works
The async keyword is used to declare an asynchronous method. await is applied to a Task to pause the method execution until it completes, without blocking the current thread.
public async Task<string> GetDataAsync(){ // Simulating an asynchronous HTTP request using var client = new HttpClient(); string result = await client.GetStringAsync("https://api.example.com/data"); return result;}
public async Task ProcessAsync(){ string data = await GetDataAsync(); Console.WriteLine($"Data received: {data}");}It is important to understand: await does not block the thread. It returns control to the calling code, and when the task completes, the method continues execution from the same point.
Return types of asynchronous methods
An asynchronous method can return one of three types:
Task— for methods that do not return a valueTask<T>— for methods that return a value of type TValueTask<T>— for high-performance scenarios where the result is often available synchronously
// Without a return valuepublic async Task SaveDataAsync(string data){ await File.WriteAllTextAsync("data.txt", data);}
// With a return valuepublic async Task<int> CalculateSumAsync(int a, int b){ await Task.Delay(100); // Simulating a long-running operation return a + b;}
// ValueTask for optimizationpublic async ValueTask<bool> CheckCacheAsync(string key){ if (_cache.ContainsKey(key)) return true; await Task.Delay(50); return false;}Advanced work with Task
The Task class is the foundation of asynchronous programming in C#. Let's look at the most useful methods and patterns.
Parallel task execution
The Task.WhenAll and Task.WhenAny methods allow you to efficiently manage multiple asynchronous operations.
public async Task ProcessMultipleRequestsAsync(){ var task1 = FetchDataAsync("url1"); var task2 = FetchDataAsync("url2"); var task3 = FetchDataAsync("url3"); // Waiting for all tasks to complete await Task.WhenAll(task1, task2, task3); Console.WriteLine("All requests completed");}
public async Task<string> GetFirstResponseAsync(){ var task1 = FetchDataAsync("url1"); var task2 = FetchDataAsync("url2"); // Waiting for the first task to complete Task<string> completedTask = await Task.WhenAny(task1, task2); return await completedTask;}Cancelling asynchronous operations
Blogs
Book Recommendations
Read also
Asynchronous Programming in JavaScript: A Complete Guide