Async and Await in C#
C# provides built-in support for asynchronous programming through the async and await keywords. They allow developers to write code that performs non-blocking operations, such as file I/O, network calls or database queries, without freezing the main thread.
Asynchronous programming improves application responsiveness, especially in UI-based and server-side applications where blocking operations can degrade performance.
Key Points
- async and await simplify writing asynchronous code.
- The async modifier is applied to a method, lambda or anonymous function.
- The await keyword is used inside an async method to pause execution until the awaited task is complete.
- An async method usually returns Task, Task<TResult> or void (only for event handlers).
- Execution does not block the calling thread while awaiting a task.
Syntax
async Task MethodName()
{
await SomeAsyncOperation();
}
How Async and Await Work
- The method marked with async can use await inside its body.
- When await is encountered, the control is returned to the caller until the awaited task finishes.
- After completion, execution resumes from the point where it was paused.
Example 1: Asynchronous File Read
using System;
using System.IO;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
string path = "example.txt";
string content = await ReadFileAsync(path);
Console.WriteLine("File Content:\n" + content);
}
static async Task<string> ReadFileAsync(string filePath)
{
using (StreamReader reader = new StreamReader(filePath))
{
return await reader.ReadToEndAsync();
}
}
}
Explanation:
- ReadFileAsync is marked with async and returns Task<string>.
- ReadToEndAsync is awaited, so the method does not block the main thread.
- The program reads the file content asynchronously.
Example 2: Simulating Delay
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("Task started...");
await Task.Delay(3000); // Non-blocking delay
Console.WriteLine("Task finished after delay.");
}
}
Output:
Task started...
Task finished after delay.
Here, Task.Delay simulates a non-blocking pause without freezing the program.
Example 3: Async with Return Type
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
int result = await CalculateSumAsync(10, 20);
Console.WriteLine("Sum: " + result);
}
static async Task<int> CalculateSumAsync(int a, int b)
{
return await Task.Run(() => a + b);
}
}
Explanation:
- The method CalculateSumAsync returns Task<int>.
- The computation runs inside Task.Run and the result is awaited.
Rules
- An async method must have at least one await inside, otherwise, it executes synchronously.
- Use async Task for methods that perform asynchronous operations without returning a result.
- Use async Task<TResult> for methods that return a result.
- Avoid async void except in event handlers.
- Avoid blocking calls like .Result or .Wait() on tasks as they may cause deadlocks.
Benefits of Async and Await
- Non-blocking execution.
- Better responsiveness in UI applications.
- Efficient resource utilization in server applications.
- Cleaner code compared to traditional callbacks.