【发布时间】:2019-03-02 02:16:13
【问题描述】:
我最近遇到了与此处描述的类似的死锁问题:An async/await example that causes a deadlock
此问题与以下问题不重复。
因为我可以访问代码的异步部分,所以我可以使用@stephen-cleary 在他的博客文章中描述的解决方案:http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
所以我在第一个等待的任务中添加了ConfigureAwait(false)。
由于受影响的代码数量,我无法使用第二种解决方案(将async 放在任何地方)。
但我一直在问自己,如果我无法修改调用的异步代码(例如:extern API)怎么办。
所以我想出了一个避免死锁的解决方案。一种Join 任务的方法。 Join 我的意思是一种等待目标任务结束而不阻塞当前线程上运行的其他任务的方法。
代码如下:
public static class TaskExtensions
{
public static void Join(this Task task)
{
var currentDispatcher = Dispatcher.CurrentDispatcher;
while (!task.IsCompleted)
{
// Call back the dispatcher to allow other Tasks on current thread to run
currentDispatcher.Invoke(delegate { }, DispatcherPriority.SystemIdle);
}
}
}
我想强调一下,我不确定这个方法的正确名称是Join。
我的问题是为什么Task.Wait() 没有以这种方式实现,或者可以选择以这种方式使用?
【问题讨论】:
-
任务以前向传播的方式实现。当一个任务完成时,它会通知 something 并且正在等待的新任务由此开始。此外,记录使用
.Wait()等待任务完成以阻塞线程直到任务完成,您的实现基本上是“解除阻塞线程”。那你为什么不把你的代码安排在任务完成后作为一个新任务运行,从第一个任务开始呢?基本上,框架中有 很好的机制可以完全按照您的意愿行事。 -
另外,请注意“为什么他们以这种特殊方式实现this?”是一个经常被关闭为“主要基于意见”的问题,因为我们很少有机会获得原始设计师的智慧,通常只能猜测并提出好的和似是而非的 这样做的原因。
-
正确的答案是“在任何地方添加
async”,尽管您显然对此很反感。人们经常观察到,当您采用此功能时,它往往会病毒式地影响整个调用堆栈。 -
虽然您的代码可以在 Web 应用程序中运行,但您可以使用原始问题中的代码以相同的方式死锁 winforms 应用程序,在这种情况下,“解锁”代码将不得不启动 Windows消息而不是执行任务,因此这绝不是一个通用的解决方案,因此是他们不只是将 .Wait 实现为轮询结构的合理原因之一。这取决于您的场景如何您必须进行轮询。
-
最后,在此之后,我将停止评论,现在在 2018 年,语言和运行时将专注于创建好的和 高性能 解决方案,并绑定一个线程等待以轮询方式执行任务并不是创建高性能和可扩展解决方案的好方法。使其在任何地方都异步是。
标签: c# async-await task threadpool