转:https://blog.csdn.net/wpwalter/article/details/78370706
原文一部分内容:
wait线程锁:主执行线程调用子线程后挂起等待子线程结果,子线程又需要切换到主线程或者等待主线程返回,从而导致两个线程均处在阻塞状态(死锁)
调用 Task.Wait()
或者 Task.Result
立刻产生死锁的充分条件:
1. 调用 Wait()
或 Result
的代码位于 UI 线程;
2. Task
的实际执行在其他线程。
总结不会造成死锁的充分条件:
1. 异步操作执行完后不需要回到原有线程(例如非 UI 线程和控制台线程);
2. 异步操作不需要单独的线程执行任务。
如何避免死锁?
明确了会造成死锁的条件和不会造成死锁的条件后,我们只需要做到以下几点即可避免死锁了:
在 UI 线程,如果使用了 async/await,就尽量不要再使用 Task.Wait()/Task.Result 了,就一直异步一条路走到黑好了(微软称其为 Async All the Way)。
如果可能,尽量在异步任务后添加 .ConfigureAwait(false);这样,异步任务后面继续执行的代码就不会回到原 UI 线程了,而是直接从线程池中再取出一个线程执行;这样,即便 UI 线程后续可能有别的原因造成阻塞,也不会产生死锁了。
把原来的代码改成这样,就不会死锁了:
await DoAsync();
async Task DoAsync()
{
await Task.Run(() => { });
}
没错!只能是一路 async/await。微软将其描述为:async/await 会像病毒一样在你的代码中传播。
Others have also noticed the spreading behavior of asynchronous programming and have called it “contagious” or compared it to a zombie virus.
这句话的原文参见:Async/Await - Best Practices in Asynchronous Programming
为了防止真的有代码的调用者使用 Wait(),我们也得写出防 SB 的代码来:
async Task DoAsync()
{
await Task.Run(() => { }).ConfigureAwait(false);
}