简体   繁体   English

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

[英]Why ConfigureAwait(false) does not work while Task.Run() works?

I'm calling an async library method with ConfigureAwait(false).我正在使用 ConfigureAwait(false) 调用异步库方法。 But, I still end up with deadlock.但是,我仍然以僵局告终。 (I'm using it in ASP.NET controller API) But, if I use same method wrapped into Task.Run() it works fine. (我在 ASP.NET 控制器 API 中使用它)但是,如果我使用包装到 Task.Run() 中的相同方法,它工作正常。

My understanding is, If library method is not using ConfigureAwait internally then adding ConfigureAwait won't solve the problem as in the library call it will result in deadlock (we block on it by using .Result).我的理解是,如果库方法在内部没有使用 ConfigureAwait,那么添加 ConfigureAwait 不会解决问题,因为在库调用中它会导致死锁(我们使用 .Result 阻止它)。 But, if that's the case why does it work in Task.Run() as it will fail to continue in same context/thread.但是,如果是这种情况,为什么它会在 Task.Run() 中工作,因为它将无法在相同的上下文/线程中继续。

This article talks about it.这篇文章谈到了它。 Btw, I have ready plenty of articles by Stephen Cleary.顺便说一句,我已经准备好了斯蒂芬克利里的大量文章。 But, why Task.Run() works is a mystery.但是,为什么 Task.Run() 起作用是个谜。

Code snippet:代码片段:

// 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);
}

I believe Lukazoid is correct.我相信 Lukazoid 是正确的。 To put it another way...换个说法...

// 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;
}

You can't just stick a ConfigureAwait(false) at one level and have it magically prevent deadlocks.你不能只在一个级别上粘贴一个ConfigureAwait(false)并让它神奇地防止死锁。 ConfigureAwait(false) can only prevent deadlocks if it is used by every await in the transitive closure of that method and all methods it calls.只有在该方法及其调用的所有方法的传递闭包中的每个await都使用它时, ConfigureAwait(false)才能防止死锁。

In other words, ConfigureAwait(false) needs to be used for every await in Create (which it is), and it also needs to be used for every await in CreateDocumentAsync (which we don't know), and it also needs to be used for every await in every method that CreateDocumentAsync calls, etc.换句话说, ConfigureAwait(false)需要用于Create每个await (它是),并且它也需要用于CreateDocumentAsync每个await (我们不知道),并且它也需要用于CreateDocumentAsync调用的每个方法中的每个await等。

This is one reason why it is such a brittle "solution" to the deadlock problem.这就是为什么它是死锁问题的一个如此脆弱的“解决方案”的原因之一。

In the first example, the implementation of Client.CreateDocumentAsync is deadlocking because it is trying to execute a continuation using the current SynchronizationContext .在第一个示例中, Client.CreateDocumentAsync的实现是死锁的,因为它尝试使用当前SynchronizationContext执行延续。

When using Task.Run , the delegate will be invoked on a ThreadPool thread, this means there will be no current SynchronizationContext so all continuations will be resumed using a ThreadPool thread.使用Task.Run ,将在 ThreadPool 线程上调用委托,这意味着将没有当前SynchronizationContext因此将使用 ThreadPool 线程恢复所有延续。 This means it will not deadlock.这意味着它不会死锁。

Out of interest, why is your CreateConfig method not async?出于兴趣,为什么您的CreateConfig方法不是异步的? Most recent versions of MVC and WebAPI support asynchronous methods, getting rid of the .Result would be the best solution.最近的MVC和的WebAPI支持异步方法的版本,摆脱的.Result将是最好的解决方案。

Just an observation: i also noticed that doing this will also result in a deadlock只是一个观察:我也注意到这样做也会导致死锁

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);
}

So even running things on the thread pool may not be the ultimate solution.所以即使在线程池上运行东西也可能不是最终的解决方案。 It seems an equally import factor to consider is what SynchonizationContext was in effect when the async method's task was created.它似乎认为一个同样进口的因素是什么SynchonizationContext实际上是在创建异步方法的任务时。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM