更新这个问题的目的是得到关于 Task.Run()
和死锁的简单答案 . 我非常理解不混合异步和同步的理论推理,我将它们铭记于心 . 我只需要一个人的技术答案......
我有一个需要调用异步方法的 Dispose()
方法 . 由于95%的代码都是异步的,因此重构不是最佳选择 . 有一个 IAsyncDisposable
(以及其他功能)'s supported by the framework would be ideal, but we'还没有 . 所以在同一时间,我需要找到一种可靠的方法从同步方法调用异步方法而不会发生死锁 .
我宁愿不使用 ConfigureAwait(false)
因为这使得责任分散在我的整个代码中,以便被调用者以某种方式行事,以防调用者是同步的 . 我是一个不正常的开玩笑者 .
在阅读了Stephen Cleary关于另一个问题的评论之后, Task.Run()
总是在线程池上安排甚至异步方法,这让我想到了 .
在ASP.NET中的.NET 4.5或任何其他同步上下文中,将任务调度到当前线程/同一线程,如果我有一个异步方法:
private async Task MyAsyncMethod()
{
...
}
我想从同步方法中调用它,我可以使用 Task.Run()
和 Wait()
来避免死锁,因为它将异步方法排队到线程池吗?
private void MySynchronousMethodLikeDisposeForExample()
{
// MyAsyncMethod will get queued to the thread pool
// so it shouldn't deadlock with the Wait() ??
Task.Run((Func<Task>)MyAsyncMethod).Wait();
}
5 回答
您似乎了解了问题中涉及的风险,因此我将跳过讲座 .
要回答你的实际问题:是的,你可以使用
Task.Run
将该工作卸载到没有SynchronizationContext
的ThreadPool
线程,因此没有真正的死锁风险 .However ,使用另一个线程只是因为它没有SC有点像黑客并且可能是一个昂贵的,因为在
ThreadPool
上完成的工作安排有其成本 .一个更好,更清晰的解决方案IMO将暂时使用
SynchronizationContext.SetSynchronizationContext
删除SC并在之后恢复它 . 这可以很容易地封装到IDisposable
中,因此您可以在using
范围内使用它:用法:
这个代码不会因为你在问题中突出显示的原因而死锁 - 代码总是在没有同步上下文的情况下运行(因为使用线程池),而
Wait
将简单地阻塞线程直到/ if方法返回 .当我必须同步调用异步方法并且线程可以是UI线程时,这是我避免死锁的方法:
使用小型自定义同步上下文,同步功能可以等待异步功能的完成,而不会产生死锁 . 原始线程被保留,因此sync方法在调用异步函数之前和之后使用相同的线程 . 这是WinForms应用程序的一个小例子 .
如果绝对必须从同步方法调用异步方法,请确保在异步方法调用中使用
ConfigureAwait(false)
以避免捕获同步上下文 .这应该成立,但充其量只是摇摇欲坠 . 我建议考虑重构 . 代替 .