如何在没有等待的情况下等待异步操作的结果?

时间:2016-04-04 12:16:31

标签: c# async-await

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

如果我将A()标记为try catch我永远不会进入public async void A()

,我会在async中致电catch

1 个答案:

答案 0 :(得分:3)

好吧,首先你想要一个代表所有操作的Task。最简单的方法是使用一点LINQ:

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

然后,您理想地希望await该任务。如果无法做到,可以尝试

task.GetAwaiter().GetResult();

但是,请确保没有任何任务具有线程关联性 - 这是获得死锁的好方法。在任务本身需要UI线程的同时在UI线程上等待任务是一个典型的例子。

await的重点在于它允许您处理异步代码,就好像它是同步的一样。所以从外部来看,似乎你从未离开过这个方法,直到你真正到达return(或方法的结尾)。但是,为了实现此目的,您的方法必须返回Task(或Task<T>),并且被调用者必须依次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异步方法,并且所有内容都会“感觉”再次同步。不要打破链条,幻想是完美的:)