# Concurrency with Tasks and Cancellation — C#/.NET

Source: https://www.skillbyai.com/en/dotnet/a-patterns

> Run tasks concurrently, cancel them, and stream results asynchronously.

## Coordinating many operations

**`Task.WhenAll`** awaits several tasks started together and returns all results, so three independent 1-second calls take about 1 second rather than 3. **`Task.WhenAny`** completes when the first task finishes, useful for timeouts or racing replicas. **Cancellation** is cooperative: pass a **`CancellationToken`** down through every async method, and libraries such as `HttpClient` and EF Core stop work when it is cancelled, throwing `OperationCanceledException`. Create tokens with a `CancellationTokenSource`, for example `new CancellationTokenSource(TimeSpan.FromSeconds(5))` for a timeout. **`IAsyncEnumerable<T>`** with `await foreach` streams results that arrive asynchronously, such as pages of an API. **`Parallel.ForEachAsync`** processes many items with a limit on concurrency. **`ValueTask<T>`** can avoid an allocation when results are often available synchronously, but must not be awaited twice.

## WhenAll, a timeout and limited parallelism

The token flows into every call so a slow dependency cannot hang the request forever.

```cs
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var ct = cts.Token;

// three independent calls run concurrently
Task<decimal> priceTask = GetPriceAsync("sku-1", ct);
Task<int> stockTask = GetStockAsync("sku-1", ct);
Task<double> ratingTask = GetRatingAsync("sku-1", ct);
await Task.WhenAll(priceTask, stockTask, ratingTask);
Console.WriteLine($"{priceTask.Result} / {stockTask.Result} / {ratingTask.Result}");  // safe: already completed

// process 1,000 images, at most 8 at a time
var files = Directory.GetFiles("uploads", "*.jpg");
await Parallel.ForEachAsync(files, new ParallelOptions { MaxDegreeOfParallelism = 8, CancellationToken = ct },
    async (file, token) => await ResizeAsync(file, token));

// streaming results
await foreach (var page in GetPagesAsync(ct))
    Console.WriteLine(page.Count);
```

## Ordering at a food court

Awaiting one stall at a time is queueing at each counter in turn. `Task.WhenAll` is placing orders at three stalls, then collecting all three trays when the buzzers go off.

**Quiz:** How should a long-running async operation be stopped when the user cancels a request?

- [ ] Call Thread.Abort
- [x] Pass a CancellationToken through the call chain and let operations observe it
- [ ] Use .Result to block it
- [ ] Throw an exception from another thread

*Answer:* Pass a CancellationToken through the call chain and let operations observe it. Cancellation in .NET is cooperative via CancellationToken.
