Sponsor: Do you build complex software systems? See how NServiceBus makes it easier to design, build, and manage software systems that use message queues to achieve loose coupling. Get started for free.
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.
Ben Adams wrote a Blocking Detector for ASP.NET Core.
Start by adding the NuGet package Ben.BlockingDetector to your csproj.
At the very beginning (or the higher the better) of your Configure() method in your Startup, add the UseBlockingDetection() extension method to the IApplicationBuilder.
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).
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.