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
答案 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
异步方法,并且所有内容都会“感觉”再次同步。不要打破链条,幻想是完美的:)