Set SynchronizationContext to null instead of using ConfigureAwait(false)
I have a library that exposes synchronous and asynchronous versions of a method, but under the hood, they both have to call an async method.
The library is wrong to expose a synchronous version. Just pretend the synchronous API doesn't exist.
so to avoid deadlocks
There shouldn't be any problems with deadlocks if you call an asynchronous method that uses async
/await
. If it doesn't use ConfigureAwait(false)
, then it's not as efficient as it could be, that's all. Deadlocks due to ConfigureAwait(false)
only apply when you're trying to do sync-over-async (i.e., if you're calling the synchronous APIs from that library).
So, the easiest and simplest solution is to just ignore the synchronous APIs, which are incorrectly designed anyway:
return await MyMethodAsync();
Provided you wrap this technique in a suitably named static function, I think your suggest is significantly better than Task.Run
, even if still the lesser of two evils.
Task.Run
has a number of issues:
- It is not clear why you are using it, you want to start a new task on a web server? This will be deleted by new developers fast if there are no comments. And then boom, difficult to diagnose production issues (deadlocks).
- It starts on a new thread pool thread when it doesn't need to until it reaches its first await completed continuation.
- It makes you block synchronously for the entire Task returning function, when from your description of the problem, the blocking is actually just part of the overall task. What is being encouraged here is longer blocking over async code, this is certainly not what you want.
- If you use it multiple levels, you are multiplying the problem (with
SetSynchronizationContext
there's no harm in doing it more than once). - If it turns out that there was no blocking / deadlock where you thought there was, or it had been fixed,
Task.Run
now is introducing blocking over async, whereasSetSynchronizationContext
will not cost you anything, in addition to the optimizations it makes by not resuming on the context constantly.
I also understand there is hesitance to make any recommendation given blocking on async code should be avoided at all costs, however you have made it clear you are aware of this and this is to fix a known case of this outside of your immediate control. I think the dogmatic attitude towards this topic is damaging to the .NET ecosystem.
Setting the SynchronizationContext
to null seems hacky for me. Instead you can really delegate the work to threadpool. Use Task.Run
..
var result = Task.Run(() => MyMethodAsync()).Result;
or
var result = Task.Run(async () => await MyMethodAsync()).Result;
This avoids the deadlock and eliminates the hacky code as well.