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

本文关键字:工作 不起作用 Run ConfigureAwait false 为什么 Task | 更新日期: 2024-09-24 02:33:02

我正在使用.ConfigureAwait(false)调用async库方法。但是,我仍然以僵局告终。(我正在 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);
}

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

我相信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)粘在一个层面上,然后让它神奇地防止死锁。 ConfigureAwait(false) 只有在该方法及其调用的所有方法的传递闭包中的每个await都使用它时,才能防止死锁。

换句话说,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生效的内容。

相关文章: