C# 为什么当Task.Run()工作时ConfigureAwait(false)不工作?
我正在使用ConfigureAwait(false)调用一个异步库方法。但是,我还是以僵局告终。(我正在ASP.NET控制器API中使用它) 但是,如果我使用包装在Task.Run()中的相同方法,它就可以正常工作 我的理解是,如果库方法没有在内部使用ConfigureAwait,那么添加ConfigureAwait将无法解决问题,因为在库调用中,它将导致死锁(我们使用.result阻止它)。但是,如果是这样,为什么它在Task.Run()中工作,因为它将无法在同一上下文/线程中继续 这就是它。顺便说一句,我准备了很多斯蒂芬·克利里的文章。但是,为什么Task.Run()有效还是个谜 代码段: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()中工作,因为它将无法在同一上下文/线程中继续 这就是它。
// 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()不会产生任何问题。@KrunalModiConfigureAwait(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:是的,从某种意义上说,如果你要使用它,最好一路使用它。