C# 为什么当Task.Run()工作时ConfigureAwait(false)不工作?

C# 为什么当Task.Run()工作时ConfigureAwait(false)不工作?,c#,async-await,configureawait,C#,Async Await,Configureawait,我正在使用ConfigureAwait(false)调用一个异步库方法。但是,我还是以僵局告终。(我正在ASP.NET控制器API中使用它) 但是,如果我使用包装在Task.Run()中的相同方法,它就可以正常工作 我的理解是,如果库方法没有在内部使用ConfigureAwait,那么添加ConfigureAwait将无法解决问题,因为在库调用中,它将导致死锁(我们使用.result阻止它)。但是,如果是这样,为什么它在Task.Run()中工作,因为它将无法在同一上下文/线程中继续 这就是它。

我正在使用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);
}
//此创建方法导致死锁
公共异步任务创建(MyConfig配置)
{
Document doc=await Client.createdocumentsync(CollectionUri,config).ConfigureAwait(false);
返回单据Id;
}
//使用工作正常的Task.Run(),为什么??
创建公共字符串(MyConfig配置)
{
Document doc=Task.Run(()=>Client.createdocumentsync(CollectionUri,config)).Result;
返回单据Id;
}
[HttpPost]
公共操作结果CreateConfig(MyConfig配置)
{
字符串id=Create(config).Result;
返回Json(id);
}

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

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


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

我相信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;
}
//此创建方法导致死锁
公共异步任务创建(MyConfig配置)
{
Document doc=await Client.createdocumentsync(CollectionUri,config).ConfigureAwait(false);
返回单据Id;
}
您不能只在一个级别上粘贴
ConfigureAwait(false)
,然后让它神奇地防止死锁
ConfigureAwait(false)
仅当该方法及其调用的所有方法的传递闭包中的每个
await
都使用它时,才能防止死锁

换言之,
ConfigureAwait(false)
需要用于
Create
中的每个
await
(它是),也需要用于
createdocumentsync
中的每个
await
(我们不知道),它还需要用于
createDocumentSync
调用的每个方法中的每个
wait
,等等


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

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

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);
}
私有字符串创建(任务)
{
var doc=Task.Run(()=>Task.Result);
返回单据Id;
}
[HttpPost]
公共操作结果CreateConfig(MyConfig配置)
{
var task=Client.CreateDocumentAsync(CollectionUri,config);
var id=创建(任务).Result;
返回Json(id);
}

因此,即使在线程池上运行东西也可能不是最终的解决方案。当<<强>异步方法的任务< /强>被创建时,考虑代码<>同步化上下文< /代码>是一个同样重要的因素。

不应该使用CythuraAsAuto(FALSE)确保使用线程池继续?是的,我已经使CreateConfig方法异步。但是,想更准确地理解为什么Task.Run()不会产生任何问题。@KrunalModi
ConfigureAwait(false)
只配置continuations以使用线程池,不是调用
Client.CreateDocumentAsync
@KrunalModi将
ConfigureAwait
与异步调用一起使用是没有意义的,该异步调用在内部具有另一个不与
ConfigureAwait
一起使用的异步调用
ConfigureAwait(false)
几乎应该在任何地方使用,尤其是在库中。只有顶层调用(UI同步上下文中的调用)不需要
ConfigureAwait(false)
,因为延续应该在UI同步上下文中执行。对于不在内部使用
ConfigureAwait(false)
的库,使用
await Task.Run(()=>…)
await Task.Run(()=>…).ConfigureAwait(false)
是一个很好的解决方法。每个异步调用都应该充分记录在这方面,以便做出正确的决定。使用
await[async call].ConfigureAwait(false)
await Task.Run(async()=>await[async call]).ConfigureAwait(false)
@thanassioannidis:理论上这很好,但实际上缺少
ConfigureAwait(false)
通常是一个错误。所以文档可能是错误的。我们可以说“一路等待(错误)”就像“一路等待”一样吗?@batmaci:是的,从某种意义上说,如果你要使用它,最好一路使用它。