I've reviewed dozens of .NET Core backends that suffered mysterious slowdowns and memory spikes.
Every time, the root cause was the same: bad async code.
These async mistakes aren't obvious.
They don't throw errors. They just slowly kill your app.
Here are the 10 most common async pitfalls I see (and how to fix them):
Stop making these costly errors 👇
𝟭/ 𝗡𝗼𝘁 𝘂𝘀𝗶𝗻𝗴 𝗖𝗮𝗻𝗰𝗲𝗹𝗹𝗮𝘁𝗶𝗼𝗻𝗧𝗼𝗸𝗲𝗻
↳ Requests keep running after clients disconnect
↳ Wasted CPU cycles and memory leaks
✅ Always pass CancellationToken through your async chain
𝟮/ 𝗕𝗹𝗼𝗰𝗸𝗶𝗻𝗴 𝗼𝗻 𝗮𝘀𝘆𝗻𝗰 𝗰𝗼𝗱𝗲
↳ Using .Result, Wait(), GetAwaiter() .GetResult()
↳ Thread pool starvation = application freezes
✅ Use await consistently, never block on async calls
𝟯/ 𝗡𝗼 𝘁𝗶𝗺𝗲𝗼𝘂𝘁𝘀 𝗼𝗻 𝗼𝘂𝘁𝗯𝗼𝘂𝗻𝗱 𝗿𝗲𝗾𝘂𝗲𝘀𝘁𝘀
↳ HttpClient calls hang forever waiting for responses
↳ Resource exhaustion brings down your entire service
✅ Set HttpClient .Timeout and use CancellationTokenSource with timeout
𝟰/ 𝗡𝗼𝘁 𝗺𝗮𝗸𝗶𝗻𝗴 𝘁𝗵𝗲 𝗲𝗻𝘁𝗶𝗿𝗲 𝗰𝗮𝗹𝗹 𝗰𝗵𝗮𝗶𝗻 𝗮𝘀𝘆𝗻𝗰
↳ Mixing sync/async patterns breaks everything
↳ Deadlocks and poor scalability under load
✅ Go async all the way: controller → service → repository
𝟱/ 𝗨𝘀𝗶𝗻𝗴 𝗧𝗮𝘀𝗸.𝗥𝘂𝗻 𝗶𝗻𝘀𝗶𝗱𝗲 𝗰𝗼𝗻𝘁𝗿𝗼𝗹𝗹𝗲𝗿𝘀/𝘀𝗲𝗿𝘃𝗶𝗰𝗲𝘀
↳ Offloading I/O work to thread pool unnecessarily
↳ Context switching overhead kills performance
✅ Use Task .Run only for CPU-intensive work, not I/O operations
𝟲/ 𝗕𝘂𝗳𝗳𝗲𝗿𝗶𝗻𝗴 𝗵𝘂𝗴𝗲 𝗽𝗮𝘆𝗹𝗼𝗮𝗱𝘀 𝗶𝗻𝘀𝘁𝗲𝗮𝗱 𝗼𝗳 𝘀𝘁𝗿𝗲𝗮𝗺𝗶𝗻𝗴
↳ Loading 500MB responses into memory at once
↳ OutOfMemoryException crashes your app
✅ Use IAsyncEnumerable and Stream .CopyToAsync instead
𝟳/ 𝗨𝘀𝗶𝗻𝗴 𝗧𝗮𝘀𝗸.𝗪𝗵𝗲𝗻𝗔𝗹𝗹 𝘄𝗶𝘁𝗵𝗼𝘂𝘁 𝗹𝗶𝗺𝗶𝘁𝘀
↳ Firing 1000 concurrent requests to external APIs
↳ Rate limiting and cascade failures
✅ Control concurrency with small chunks or Parallel .ForEachAsync
𝟴/ 𝗧𝗵𝗲 𝗮𝘀𝘆𝗻𝗰 𝘃𝗼𝗶𝗱 𝘁𝗿𝗮𝗽
↳ async void methods outside event handlers
↳ Unhandled exceptions crash your entire process
✅ Always return Task or Task from async methods
𝟵/ 𝗙𝗶𝗿𝗲-𝗮𝗻𝗱-𝗳𝗼𝗿𝗴𝗲𝘁 𝗱𝗶𝘀𝗮𝘀𝘁𝗲𝗿𝘀
↳ Starting background tasks without proper tracking
↳ Silent failures and resource leaks everywhere
✅ Use OutBox or BackgroundService to process background tasks
𝟭𝟬/ 𝗠𝗶𝘀𝘀𝗶𝗻𝗴 𝗖𝗼𝗻𝗳𝗶𝗴𝘂𝗿𝗲𝗔𝘄𝗮𝗶𝘁(𝗳𝗮𝗹𝘀𝗲)
↳ Library code captures synchronization context unnecessarily
✅ Use ConfigureAwait(false) in all library async methods
👉 Join 𝟮𝟱,𝟬𝟬𝟬+ people in my .NET Newsletter. Weekly best practices, real-world examples, and pro tips to craft better software today!
𝗕𝗼𝗻𝘂𝘀: every subscriber gets a PDF with 200+ .NET interview questions
—
♻️ Repost to help others avoid common async mistakes
➕ Follow me ( @AntonMartyniuk ) to improve your .NET Skills