This looks innocent:
await Task.WhenAll(requests.Select(r => ProcessAsync(r)));
But if requests = 10,000 items →
you just spawned 10,000 concurrent tasks.
✅ Safe Pattern
using var semaphore = new SemaphoreSlim(10);
await Task.WhenAll(requests.Select(async r =>
{
await semaphore.WaitAsync();
try { await ProcessAsync(r); }
finally { semaphore.Release(); }
}));
Why this matters
-
Prevents thread starvation
-
Protects downstream services
-
Stabilizes latency under load
