C# & .NET
Understanding Async, Await, and Tasks in C#
Asynchrony is about waiting efficiently, not making every operation run on a new thread.
// you will learn
- Explain Task and await
- Compose independent asynchronous operations
- Propagate cancellation
What a Task represents
A Task<T> represents the eventual completion of an operation producing T. An asynchronous I/O operation can wait without occupying a thread for the entire wait. await resumes the method when the operation completes; it does not magically parallelize the method.
Start independent work before awaiting
public async Task<string[]> LoadBothAsync(
HttpClient client, CancellationToken token)
{
Task<string> first = client.GetStringAsync("/api/first", token);
Task<string> second = client.GetStringAsync("/api/second", token);
return await Task.WhenAll(first, second);
}The two HTTP operations are started before either result is awaited. Task.WhenAll completes when both complete. With shared state or rate-limited services, concurrency must be bounded rather than expanded without limit.
Handle failure and cancellation
Pass a cancellation token through every layer that supports it. Catch an exception only where you can recover or add meaningful context. Avoid .Result and .Wait() on an async call path: blocking wastes threads and can deadlock in synchronization-context environments.
I/O-bound is not CPU-bound
CPU-heavy calculation still uses a thread. Task.Run can keep a desktop UI responsive, but it does not turn CPU work into free capacity on a web server. Measure the bottleneck before choosing concurrency.
Exercise
Add a five-second timeout using CancellationTokenSource. Test a successful call, one failing endpoint, and cancellation. Explain what happens to the other operation if one faults.