最近读到一篇异步转同步的文章,发现其中没有考虑到异步转同步过程中发生的死锁问题,所以特地在本文说说异步转同步过程中的死锁问题。
文章作者 林德熙 已经修复了描述:
什么情况下会产生死锁?
调用 Task.Wait()
或者 Task.Result
立刻产生死锁的充分条件:
- 调用
Wait()
或Result
的代码位于 UI 线程; Task
的实际执行在其他线程,且需要返回 UI 线程。
死锁的原因:
UWP、WPF、Windows Forms 程序的 UI 线程都是单线程的。为了让使用了 async
/await
的代码像使用同步代码一样简单,WPF 程序的 Application
类在构造的时候会将主 UI 线程 Task
的同步上下文设置为 DispatcherSynchronizationContext
的实例,这在我的另一篇文章 Task.Yield 中也有过说明。
当 Task
的任务结束时,会从 AsyncMethodStateMachine
中调用 Awaiter
的 OnComplete()
方法,而 await
后续方法的执行靠的就是 OnComplete()
方法中一层层调用到 DispatcherSynchronizationContext
里的 Post
方法:
/// <summary>
/// Asynchronously invoke the callback in the SynchronizationContext.
/// </summary>
public override void Post(SendOrPostCallback d, Object state)
{
// Call BeginInvoke with the cached priority. Note that BeginInvoke
// preserves the behavior of passing exceptions to
// Dispatcher.UnhandledException unlike InvokeAsync. This is