It’s pretty easy to write some bad async code, especially when you first start using async/await. Async/await is pretty viral in .NET, which means it generally goes all the way through the stack. This can be challenging if you are trying to add async/await to an existing app and you usually end up adding sync over async code.

If you don’t use async/await correctly, and end up writing sync-over-async code, you’ll ultimately end up causing ThreadPool starvation.

Sync over Async

The term refers to making an async call but not awaiting it. Often time this is caused by calling .Wait() or .Result on the returned Task.

View the code on Gist.

Ben.BlockingDetector

Ben Adams wrote a Blocking Detector for ASP.NET Core.

Start by adding the NuGet package Ben.BlockingDetector to your csproj.

View the code on Gist.

At the very beginning (or the higher the better) of your Configure() method in your Startup, add the UseBlockingDetection() extension method to the IApplicationBuilder.

View the code on Gist.

That’s it. Really. Now when we run our application and hit the /sync-over-async route from the example above, a warning is logged in the console (or however you have logging configured).

I’ve added the red arrow to point out our sync-over-async method in the call stack.

Microsoft.VisualStudio.Threading.Analyzers

Another great tool is the Microsoft.VisualStudio.Threading.Analyzers NuGet Package.

Static code analyzer to detect common mistakes or potential issues regarding threading and async coding.

Simply add it the PackageReference to your csproj and by default, you will immediately be getting warnings about sync over async calls. Here’s a screenshot in JetBrains Rider (which also can use Roslyn analyzers) that is notifying us of the issue.

Are you using any other analyzers or packages to detect blocking or problematic async code? Let me know in the comments or on Twitter.

Related Posts: I’ve also written a post on Lazy Async which try’s to solve the issue of deferring doing IO until it’s first needed.