【问题标题】:Using ConfigureAwait(false) on tasks passed in to Task.WhenAll() fails对传递给 Task.WhenAll() 的任务使用 ConfigureAwait(false) 失败
【发布时间】:2015-08-20 15:49:33
【问题描述】:

我正在尝试决定如何等待所有异步任务完成。

这是我目前拥有的代码

[HttpGet]
public async Task<JsonResult> doAsyncStuff()
{
  var t1 = this.service1.task1();
  var t2 = this.service2.task2();
  var t3 = this.service3.task3();
  var t4 = this.service4.task4();

  await Task.WhenAll(t1,t2,t3,t4);
  return this.Json(new {redirect = true, href = Url.Action("Blah")}, JsonRequestBehavior.AllowGet);
}

我很确定同步上下文不相关,所以我尝试了这个。

[HttpGet]
public async Task<JsonResult> doAsyncStuff()
{
  var t1 = this.service1.task1().ConfigureAwait(false);
  var t2 = this.service2.task2().ConfigureAwait(false);
  var t3 = this.service3.task3().ConfigureAwait(false);
  var t4 = this.service4.task4().ConfigureAwait(false);

  await Task.WhenAll(t1,t2,t3,t4);
  return this.Json(new {redirect = true, href = Url.Action("Blah")}, JsonRequestBehavior.AllowGet);
}

现在的问题是 Task.WhenAll 的参数无效,因为它不会接受已配置的任务 Awaiatables。

所以Task.WhenAll需要替换成这个

await t1; await t2; await t3; await t4;

这对我来说似乎不正确,但几乎任何人都对 ConfigureAwait 有什么要说的,它是“使用它,如果它没有出错”。据我所知(我没有编写任务),它们不使用同步上下文,也不依赖它。

需要注意的是,我希望 task1 到 task4 同时运行,因为它们不依赖于前一个完成。因此,我不想在每个任务前等待。但我不想在所有 4 个完成之前返回 Json 响应,这就是为什么我目前在当前代码中有 await Task.WhenAll()。

【问题讨论】:

  • 我更新了您的标题,使其更能代表您遇到的实际问题和问题,如果您愿意,您可以将其改回,但“哪个更好”的问题不被接受,并且很可能在人们没有阅读您的实际问题的情况下引起下意识的反应。
  • 这是“配置等待”,而不是“配置任务”。
  • @StephenCleary 当您在任务上使用 .ConfigureWait(false) 时,它会将任务变为已配置的任务等待对象。 msdn.microsoft.com/en-us/library/… 我的意思是 Task.WhenAll 不适用于所述对象。
  • @vipero07:对。我是说命名是正确使用的指示。 Task.WhenAll 处理任务;它根本不使用await
  • @StephenCleary 啊,我现在跟着。是的,好点。老实说,ConfigureAwait 是一种奇怪的名称,因为它读取 Configure Await false (比如......不要配置等待)。是的,我知道 false 是为了在捕获的上下文中继续,但它不是这样读的。

标签: c# asynchronous async-await


【解决方案1】:

当你实际执行await时,你只需要ConfigureAwait,正确的形式应该是

[HttpGet]
public async Task<JsonResult> doAsyncStuff()
{
  var t1 = this.service1.task1();
  var t2 = this.service2.task2();
  var t3 = this.service3.task3();
  var t4 = this.service4.task4();

  await Task.WhenAll(t1,t2,t3,t4).ConfigureAwait(false);
  return this.Json(new {redirect = true, href = Url.Action("Blah")}, JsonRequestBehavior.AllowGet);
}

【讨论】:

  • @YuvalItzchakov 也不会在每项任务上都这样做。 ConfigureAwait 修改 await 的行为而不是您正在等待的任务的行为。在您点击await 之前,您没有机会更改 SyncContexts,所有 4 个任务仍将在线程进入的同步上下文中启动,即使他确实在每一行上放了 ConfigureAwait 然后在 await t1; await t2; await t3; await t4; 上放了结束。
  • @ScottChamberlain 你是对的。我不知怎么弄混了。
  • @ScottChamberlain 谢谢,这更有意义,看起来更干净。出于某种原因,互联网上充斥着立即等待的解决方案,在我看来,没有人想到实际异步运行多个任务似乎很奇怪。
  • @vipero07 一些支持 async/await 的东西不支持对它们的多个并发调用,即使这些调用都是在同一个线程上创建的。请参阅我几天前发布的this answer 以及随之而来的问题,以查看如何出错的示例。
猜你喜欢
  • 1970-01-01
  • 2019-11-14
  • 2014-06-24
  • 1970-01-01
  • 2021-09-22
  • 1970-01-01
  • 1970-01-01
  • 2018-02-16
相关资源
最近更新 更多