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

时间:2016-04-15 18:31:42

标签: c# async-await configureawait

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

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

这个article谈论它。顺便说一句,我已经准备好斯蒂芬克莱里的很多文章了。但是,为什么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);
}

3 个答案:

答案 0 :(得分:6)

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

使用Task.Run时,将在ThreadPool线程上调用委托,这意味着不会有当前SynchronizationContext所以所有继续都将使用ThreadPool线程恢复。这意味着它不会陷入僵局。

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

答案 1 :(得分:6)

我相信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)需要await Create await使用CreateDocumentAsync,它也需要用于每个await CreateDocumentAsync(我们不知道),并且totalTextView调用的每种方法中的每个edtPercentage都需要使用它。

这就是为什么它是如此脆弱的解决方案&#34;死锁问题。

答案 2 :(得分:1)

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

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