【问题标题】:How to wait the result of async operations without await?如何在不等待的情况下等待异步操作的结果?
【发布时间】:2016-07-23 23:29:31
【问题描述】:
void A()
{
    foreach (var document in documents)
    {
       var res = records.BulkWriteAsync(operationList, writeOptions); // res is Task<BulkWriteResult<JobInfoRecord>>
    }
}

foreach之后我想等待所有BulkWriteAsync的结果,怎么办?我不想将A() 标记为async 并执行以下操作

await records.BulkWriteAsync(operationList, writeOptions);

这是好的解决方案吗?

void A()
{
var tasks = new List<Task<BulkWriteResult<JobInfoRecord>>>();

foreach (var document in documents)
{
     var task = records.BulkWriteAsync(operationList, writeOptions);
     tasks.Add(task);
}

Task.WaitAll(tasks.ToArray());
}

如果我将public async void A() 标记为async,我将在try catch 中调用A() 我永远不会在catch

【问题讨论】:

  • 所以你只想等待最后一次操作的结果还是什么?你为什么不想让Aasync?异步调用链应该尽可能地异步。
  • 下次你应该在问题中清楚地表达你想要达到的目标。这就是为什么发布minimal reproducible example 总是一个好主意。
  • 您仍然没有说明为什么不适合您使用 async 方法。在处理异步代码时,它确实应该是默认值。
  • 这只是意味着在某些时候,您在没有正确处理孤立的Task 的情况下破坏了await 链。你为什么这样做?你在使用async void 方法吗?别。如果您不需要返回任何数据,请改用async Task
  • 我添加了更多信息。

标签: c# async-await


【解决方案1】:

嗯,首先你需要一个代表所有操作的Task。最简单的方法是使用一些 LINQ:

Task.WhenAll(documents.Select(i => records.BulkWriteAsync(...)));

然后,理想情况下,您希望await 该任务。如果不行,你可以试试

task.GetAwaiter().GetResult();

但是,请确保所有任务都没有线程亲和性 - 这是获得死锁的好方法。在 UI 线程上等待任务而任务本身需要 UI 线程就是一个典型的例子。

await 的全部意义在于它允许您像处理同步代码一样处理异步代码。所以从外面看,你似乎从未离开过方法,直到你真正到达return(或方法的结尾)。但是,要使其正常工作,您的方法必须返回 Task(或 Task&lt;T&gt;),而被调用者必须依次返回 await 您的方法。

这样的代码:

try
{
  tasks = Task.WhenAll(documents.Select(i => ...));

  await tasks;
}
catch (Exception ex)
{
  // Handle the exception
}

看起来会完全同步运行,并且所有异常都将照常处理(尽管因为我们使用的是Task.WhenAll,所以有些异常会被包裹在AggregateException中)。

然而,这实际上是不可能用 .NET 和 C# 的构建方式来处理的,所以 C# 编译器作弊 - await 基本上是一个 return,它可以保证你会得到的结果将来。发生这种情况时,控件将返回到 await 上次离开的位置。 Task 就是这样的承诺——如果你使用 async void,被调用者无法知道发生了什么,它别无选择,只能继续,就好像异步方法是一个运行后忘记的方法一样。如果你使用async Task,你可以await异步方法和一切“感觉”再次同步。不要断链,错觉就是完美的:)

【讨论】:

  • 致电ResultWaitGetResult 绝不是明智之举。如果你需要它,你的设计是错误的。
  • @Gusdor 有时这不是您的选择。也许你被一个不支持异步方法的框架调用。只要您确保避免同步问题(与任何多线程代码一样),同步等待就可以工作。当然,这是不可取的,但有时你别无选择。
  • @ivan_petrushenko 如果不能使用await,还可以。然而,GetAwaiter().GetResult() 会给你更多有意义的堆栈跟踪——它几乎假装一切都是方法调用的同步链。 Wait 不会说谎,因此您可能会得到与 GetResult 相同的堆栈跟踪,但它也可能根本没有给您任何可用的堆栈跟踪。
猜你喜欢
  • 2021-12-19
  • 2018-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-07
  • 2016-03-25
  • 2017-10-09
相关资源
最近更新 更多