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

Kru*_*odi 9 c# async-await configureawait

我正在使用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);
}
Run Code Online (Sandbox Code Playgroud)

Luk*_*oid 6

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

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

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

  • @KrunalModi`ConitigureAwait(false)`仅配置连续使用线程池,而不是调用`Client.CreateDocumentAsync` (5认同)

Ste*_*ary 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;
}
Run Code Online (Sandbox Code Playgroud)

你不能只是坚持ConfigureAwait(false)一个级别,并神奇地防止死锁.ConfigureAwait(false)只有当await该方法的传递闭包中的每一个以及它调用的所有方法都使用死锁时,才能防止死锁.

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

这就是为什么它是解决僵局问题的一个脆弱的"解决方案"的原因之一.

  • @batmaci:是的,从某种意义上说,如果你要使用它,最好一直使用它。 (2认同)