Threadpool deadlock with Task.Result Threadpool deadlock with Task.Result multithreading multithreading

Threadpool deadlock with Task.Result


You have explained your issue very well.

As you are using Task.Run then blocking on the result, you are using 1-2 threads, when really with async you want to use 0-1 threads.

If you are using Task.Run too liberally through your code then there is a chance you'll have multiple layers of blocking threads, making thread usage get really ugly fast, and you'll hit the maximum capacity as you've described.

As an aside, forget trying to find async deadlocks in unit tests (or console apps) as it requires as non-default SynchronizationContext.

The best and right solution would be to make everything top-to-bottom async or sync, but given you are constrained, I'd suggest investigating this wonderful library from the Microsoft vs team and look at JoinableTaskFactory.Run(...), this will run continuations on the blocking thread, and plays well when you nest this pattern at multiple levels. Using this approach, you will get closer to the synchronous equivalent code.

To reiterate, these techniques are workarounds, and if you are justifying these workarounds by respecting the existing code, the best way to respect it is to do it right, and make it fully sync, or top-to-bottom async.


You can safely use short form (Foo().Result) instead of Task.Run(() => Foo()).Result if you disable aspnet:UseTaskFriendlySynchronizationContext setting:

<appSettings>   <add key="aspnet:UseTaskFriendlySynchronizationContext" value="false" /></appSettings>

Disabling task-friendly synchronization context means that HttpContext.Current would be null after any await operator - but now it is null inside of Task.Run.

Using Foo().Result instead of Task.Run(() => Foo()).Result will lead to 2 times less thread pool usage, so it can solve your problem.

Also you can use <httpRuntime> and <processModel> to configure minimum free thread pool size:

<system.web>  <processModel autoConfig="false" maxWorkerThreads="..." maxIoThreads="..." />  <httpRuntime minFreeThreads="..." /></system.web>

Note that default values are:

maxWorkerThreads = 100 per cpu

maxIoThreads = 100 per cpu

minFreeThreads = 88 per cpu