Asynchronous Programming in C#
1. What is asynchronous programming in C#?
Asynchronous programming is a programming technique that allows an application to perform time-consuming operations without blocking the calling thread while waiting for those operations to complete.
In C#, asynchronous programming is primarily implemented using:
-
async– Declares a method that can useawait. -
await– Asynchronously waits for an operation to complete without blocking the thread. -
Task– Represents an asynchronous operation that does not return a value. -
Task<T>– Represents an asynchronous operation that returns a value.
Asynchronous programming is particularly useful for I/O-bound operations, such as database queries, API calls, file operations, and network requests.
2. Why do we need asynchronous programming?
Consider an ASP.NET Core Web API application that retrieves customer details from SQL Server.
A database query takes 3 seconds to complete.
Synchronous programming
Client Request
|
v
Web API Controller
|
v
Thread executes database query
|
v
Thread BLOCKED (waiting 3 seconds)
|
v
Database returns result
|
v
Thread processes result
|
v
Response to Client
Problem: The thread remains blocked while waiting for the database response.
If many requests arrive simultaneously, the application may require more threads, increasing resource consumption and reducing scalability.
Asynchronous programming
Client Request
|
v
Web API Controller
|
v
Start async database query
|
v
await database operation
|
v
Thread released to Thread Pool
|
+------> Can serve other requests
|
v
Database operation completes
|
v
Continuation resumes
(on an available thread)
|
v
Response to Client
Advantage: The thread is not blocked during the database wait and can be used for other work.
Important: Asynchronous programming does not necessarily make an individual database query faster. Its primary advantage is improving application responsiveness and scalability.
3. Example program using async and await
Consider an application that retrieves customer information.
Complete C# example
using System;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
Console.WriteLine("Application Started");
Task<string> customerTask = GetCustomerAsync();
Console.WriteLine("Doing other work...");
string customer = await customerTask;
Console.WriteLine($"Customer: {customer}");
Console.WriteLine("Application Completed");
}
static async Task<string> GetCustomerAsync()
{
Console.WriteLine("Fetching customer...");
await Task.Delay(3000);
return "Syed Mohamed";
}
}
Output
Application Started
Fetching customer...
Doing other work...
Customer: Syed Mohamed
Application Completed
The application takes approximately 3 seconds to complete because Task.Delay(3000) simulates a 3-second asynchronous operation.
Step-by-step program explanation
Step 1: Application starts
Console.WriteLine("Application Started");
Displays the first message.
Step 2: Start asynchronous operation
Task<string> customerTask = GetCustomerAsync();
-
Calls
GetCustomerAsync(). -
The method starts executing immediately.
-
When it reaches an incomplete
await, it suspends execution. -
The caller receives a
Task<string>representing the pending result.
Step 3: Continue other work
Console.WriteLine("Doing other work...");
The calling method continues executing while the asynchronous delay is pending.
Step 4: Await the result
string customer = await customerTask;
-
Checks whether the task has completed.
-
If incomplete, suspends the calling method without blocking its thread.
-
When the task completes, execution continues and retrieves the customer name.
Step 5: Display the result
Console.WriteLine($"Customer: {customer}");
Prints the customer information.
Execution flow
Main()
|
|-- Print "Application Started"
|
|-- Call GetCustomerAsync()
| |
| |-- Print "Fetching customer..."
| |
| |-- await Task.Delay(3000)
| |
| |-- Suspend GetCustomerAsync()
| |
| +-- Return incomplete Task<string>
|
|-- Print "Doing other work..."
|
|-- await customerTask
| |
| |-- Suspend Main()
| |
| | (3 seconds elapse)
| |
| |-- GetCustomerAsync() resumes
| |
| |-- Return "Syed Mohamed"
| |
| +-- Main() resumes
|
|-- Print customer
|
+-- Application Completed
4. Difference between synchronous and asynchronous programming
|
Feature |
Synchronous |
Asynchronous |
|---|---|---|
|
Execution |
Waits for operation |
Can continue other work |
|
Thread |
Blocked during wait |
Not blocked during asynchronous wait |
|
Keywords |
Normal method calls |
|
|
Return type |
|
|
|
Scalability |
Lower for high-concurrency I/O |
Better for high-concurrency I/O |
|
Common usage |
Simple operations, CPU work |
Database, API, file I/O |
Synchronous example
public string GetCustomer()
{
Thread.Sleep(3000);
return "Syed Mohamed";
}
Thread.Sleep() blocks the current thread for 3 seconds.
Asynchronous example
public async Task<string> GetCustomerAsync()
{
await Task.Delay(3000);
return "Syed Mohamed";
}
Task.Delay() creates a non-blocking asynchronous delay.