在LINQ语句中使用async/await时实际发生了什么?

use*_*702 25 c# linq asynchronous c#-5.0

以下片段编译,但我希望它等待任务结果,而不是给我一个List<Task<T>>.

var foo = bars.Select(async bar => await Baz(bar)).ToList()
Run Code Online (Sandbox Code Playgroud)

正如这里指出的,你需要使用Task.WhenAll:

var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList();
await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)

评论指出,不需要内部asyncawait内部Select():

var tasks = foos.Select(foo => DoSomethingAsync(foo)).ToList();
Run Code Online (Sandbox Code Playgroud)

这里有一个类似的问题,有人试图其中使用异步方法Where().

因此async,await在LINQ语句中是合法的语法,但它什么都不做或者它有特定的用途?

Ste*_*ary 31

我建议您不要将其视为" async在LINQ中使用".请记住两者之间的内容:代表.几个LINQ运算符接受委托,async可用于创建异步委托.

所以,当你有一个异步方法BazAsync返回一个Task:

Task BazAsync(TBar bar);
Run Code Online (Sandbox Code Playgroud)

那么这段代码会产生一系列任务:

IEnumerable<Task> tasks = bars.Select(bar => BazAsync(bar));
Run Code Online (Sandbox Code Playgroud)

同样,如果在委托中使用async和,则await创建一个返回以下内容的异步委托Task:

IEnumerable<Task> tasks = bars.Select(async bar => await BazAsync(bar));
Run Code Online (Sandbox Code Playgroud)

这两个LINQ表达式在功能上是等效的.没有重要的区别.

就像常规的LINQ表达式一样,它IEnumerable<Task>是惰性求值的.只是,与异步方法一样BazAsync,你通常希望意外双重评价或类似的东西.因此,当您投射到一系列任务时,立即重新确定序列通常是个好主意.这将调用BazAsync源序列中的所有元素,开始执行所有任务:

Task[] tasks = bars.Select(bar => BazAsync(bar)).ToArray();
Run Code Online (Sandbox Code Playgroud)

当然,我们所做的Select就是为每个元素启动一个异步操作.如果你想等待它们全部完成,那么使用Task.WhenAll:

await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)

大多数其他LINQ运算符不能与异步委托一起干净地工作.Select非常简单:您只是为每个元素启动异步操作.

  • @EliArbel:是的.但同步抛出异步方法是一种非常不寻常的情况.如果异步方法是[骨头异常](https://blogs.msdn.microsoft.com/ericlippert/2008/09/10/vexing-exceptions/),则它们应该*仅*抛出同步异常.所以我不认为这是一个重要的用例. (2认同)

Soo*_*nts 6

它有一定的用途吗?

当然.使用async并等待LINQ语句内部,您可以执行以下操作:

var tasks = foos.Select( async foo =>
    {
        var intermediate =  await DoSomethingAsync( foo );
        return await DoSomethingElseAsync( intermediate );
    } ).ToList();
await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)

如果没有在LINQ语句中进行异步/等待,那么您不会在LINQ语句中等待任何内容,因此您无法处理结果或等待其他内容.

没有async/await,在LINQ语句中,您只是启动任务,而不是等待它们完成.它们最终仍然会完成,但是在控制将离开LINQ语句之后很久就会发生,所以你只能WhenAll在行完成之后访问它们的结果,但不能在LINQ语句中访问它们.