S8.0 What This Teaches
TaskandTask<T>as promisesasync/awaitfor non-blocking code- Running tasks concurrently with
Task.WhenAllandTask.WhenAny - Cancellation with
CancellationToken - CPU-bound work with
Task.Run - Common pitfalls: deadlocks and fire-and-forget
S8.1 Task and async/await
async method returns a Task or Task<T>.
await suspends the method until the task completes, releasing the
thread to do other work:
static async Task<string> FetchGreeting(string name)
{
await Task.Delay(100); // simulate async I/O (non-blocking)
return $"Hello, {name}!";
}
// Top-level await (C# 9+)
string msg = await FetchGreeting("Alice");
Console.WriteLine(msg);
// Void-returning async entry point
async Task Main()
{
string result = await FetchGreeting("Bob");
Console.WriteLine(result);
}
await without blocking a thread.
When the awaited task completes, execution resumes from that point.
S8.2 Running Tasks Concurrently
static async Task<int> ComputeAsync(int n)
{
await Task.Delay(n * 100); // simulate variable-length work
return n * n;
}
// Sequential - each awaits before next starts (slow)
int a = await ComputeAsync(1);
int b = await ComputeAsync(2);
int c = await ComputeAsync(3);
// Concurrent - all start immediately, wait for all to finish
int[] results = await Task.WhenAll(
ComputeAsync(1),
ComputeAsync(2),
ComputeAsync(3)
);
Console.WriteLine(results.Sum()); // 1 + 4 + 9 = 14
// WhenAny - continue as soon as the first finishes
Task<int> winner = await Task.WhenAny(ComputeAsync(3), ComputeAsync(1), ComputeAsync(2));
Console.WriteLine(await winner); // 1 (finished first)
S8.3 CancellationToken
using var cts = new CancellationTokenSource(timeout: TimeSpan.FromSeconds(2));
try
{
await DoLongWorkAsync(cts.Token);
Console.WriteLine("Done");
}
catch (OperationCanceledException)
{
Console.WriteLine("Cancelled after timeout");
}
static async Task DoLongWorkAsync(CancellationToken ct)
{
for (int i = 0; i < 10; i++)
{
ct.ThrowIfCancellationRequested(); // cooperative cancellation
await Task.Delay(500, ct);
Console.Write($"{i} ");
}
}
S8.4 CPU-Bound Work with Task.Run
async/await shines for I/O-bound operations. For CPU-bound work
(computation), use Task.Run to move it off the main thread:
static int ExpensiveCompute(int n)
{
// Simulate heavy CPU work
return Enumerable.Range(1, n).Sum();
}
// Run on thread pool, keep UI/main thread free
int result = await Task.Run(() => ExpensiveCompute(10_000_000));
Console.WriteLine(result);
Task.Run inside library code - let callers decide whether
to offload. It is mainly for top-level application code keeping a UI responsive.
S8.5 Example - All Together
// Async - parallel HTTP-like fetches with timeout and cancellation.
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
var ct = cts.Token;
var tasks = new[] { "Alice", "Bob", "Carol" }
.Select(name => FetchAsync(name, ct))
.ToList();
try
{
string[] results = await Task.WhenAll(tasks);
foreach (string r in results)
Console.WriteLine(r);
}
catch (OperationCanceledException)
{
Console.WriteLine("One or more fetches cancelled");
}
static async Task<string> FetchAsync(string name, CancellationToken ct)
{
int delay = name.Length * 200; // simulate different latencies
await Task.Delay(delay, ct);
return $"Data for {name} (took {delay}ms)";
}
S8.6 Exercise
Exercise
- Write an async method
DownloadAsync(string url)that simulates downloading data with a random delay. Run three downloads concurrently withTask.WhenAlland print the total time. - Add a
CancellationTokenwith a 1-second timeout. HandleOperationCanceledExceptionand print which downloads completed. - Use
Task.Runto compute the sum of squares of 1-1,000,000 without blocking, thenawaitthe result.
S8.7 Common Mistakes
async void - fire-and-forget
async void DoWork() { ... } // exceptions are unobserved - crash the process!
async Task DoWork() { ... } // correct - exceptions propagate via Task
async void for event handlers where the signature is forced.
All other async methods should return Task or Task<T>.
.Result or .Wait() on a task (deadlock risk)
string data = GetDataAsync().Result; // blocks; can deadlock in ASP.NET
string data = await GetDataAsync(); // non-blocking - correct
Awaiting in a loop instead of concurrently
// Sequential - slow; each waits for the previous to finish
foreach (var url in urls)
await FetchAsync(url);
// Concurrent - fast; all run at the same time
await Task.WhenAll(urls.Select(FetchAsync));
S8.8 Key Terms
| Term | Meaning |
|---|---|
| Task | Represents an ongoing or completed operation; a promise |
| Task<T> | Task that eventually produces a value of type T |
| async | Marks a method as asynchronous; enables use of await inside |
| await | Suspends the method until the awaited task completes; releases the thread |
| Task.WhenAll | Returns a task that completes when all supplied tasks complete |
| Task.WhenAny | Returns a task that completes when the first supplied task completes |
| CancellationToken | Signals cooperative cancellation to async methods |
| Task.Run | Schedules CPU-bound work on the thread pool |