繁体   English   中英

为什么 ConfigureAwait(false) 在 Task.Run() 工作时不起作用?

[英]Why ConfigureAwait(false) does not work while Task.Run() works?

我正在使用 ConfigureAwait(false) 调用异步库方法。 但是,我仍然以僵局告终。 (我在 ASP.NET 控制器 API 中使用它)但是,如果我使用包装到 Task.Run() 中的相同方法,它工作正常。

我的理解是,如果库方法在内部没有使用 ConfigureAwait,那么添加 ConfigureAwait 不会解决问题,因为在库调用中它会导致死锁(我们使用 .Result 阻止它)。 但是,如果是这种情况,为什么它会在 Task.Run() 中工作,因为它将无法在相同的上下文/线程中继续。

这篇文章谈到了它。 顺便说一句,我已经准备好了斯蒂芬克利里的大量文章。 但是,为什么 Task.Run() 起作用是个谜。

代码片段:

// This Create Method results in Deadlock
public async Task<string> Create(MyConfig config)
{
        Document doc = await Client.CreateDocumentAsync(CollectionUri, config).ConfigureAwait(false);
        return doc.Id;
}

// Uses Task.Run() which works properly, why??
public string Create(MyConfig config)
{
    Document doc = Task.Run(() => Client.CreateDocumentAsync(CollectionUri, config)).Result;
    return doc.Id;
}

[HttpPost]
public ActionResult CreateConfig(MyConfig config)
{
     string id = Create(config).Result;
     return Json(id);
}

我相信 Lukazoid 是正确的。 换个说法...

// This Create Method results in Deadlock
public async Task<string> Create(MyConfig config)
{
  Document doc = await Client.CreateDocumentAsync(CollectionUri, config).ConfigureAwait(false);
  return doc.Id;
}

你不能只在一个级别上粘贴一个ConfigureAwait(false)并让它神奇地防止死锁。 只有在该方法及其调用的所有方法的传递闭包中的每个await都使用它时, ConfigureAwait(false)才能防止死锁。

换句话说, ConfigureAwait(false)需要用于Create每个await (它是),并且它也需要用于CreateDocumentAsync每个await (我们不知道),并且它也需要用于CreateDocumentAsync调用的每个方法中的每个await等。

这就是为什么它是死锁问题的一个如此脆弱的“解决方案”的原因之一。

在第一个示例中, Client.CreateDocumentAsync的实现是死锁的,因为它尝试使用当前SynchronizationContext执行延续。

使用Task.Run ,将在 ThreadPool 线程上调用委托,这意味着将没有当前SynchronizationContext因此将使用 ThreadPool 线程恢复所有延续。 这意味着它不会死锁。

出于兴趣,为什么您的CreateConfig方法不是异步的? 最近的MVC和的WebAPI支持异步方法的版本,摆脱的.Result将是最好的解决方案。

只是一个观察:我也注意到这样做也会导致死锁

private string Create(Task<Document> task)
{
    var doc = Task.Run(() => task).Result;
    return doc.Id;
}

[HttpPost]
public ActionResult CreateConfig(MyConfig config)
{
     var task = Client.CreateDocumentAsync(CollectionUri, config);
     var id = Create(task).Result;
     return Json(id);
}

所以即使在线程池上运行东西也可能不是最终的解决方案。 它似乎认为一个同样进口的因素是什么SynchonizationContext实际上是在创建异步方法的任务时。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM