【问题标题】:What actually happens when using async/await inside a LINQ statement?在 LINQ 语句中使用 async/await 时实际发生了什么?
【发布时间】:2014-05-31 10:04:26
【问题描述】:

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

var foo = bars.Select(async bar => await Baz(bar)).ToList()

正如here指出的,你需要使用Task.WhenAll

var tasks = foos.Select(async foo => await DoSomethingAsync(foo)).ToList();
await Task.WhenAll(tasks);

a comment 指出Select() 内的asyncawait 是不需要的:

var tasks = foos.Select(foo => DoSomethingAsync(foo)).ToList();

类似的问题here 有人试图在Where() 中使用异步方法。

所以 LINQ 语句中的 asyncawait 是合法的语法,但它是什么都不做还是有一定的用途?

【问题讨论】:

    标签: c# linq asynchronous c#-5.0


    【解决方案1】:

    有没有用处

    当然。在 LINQ 语句中使用 async 和 await 你可以例如做这样的事情:

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

    如果在 LINQ 语句中没有 async/await,您将不会在 LINQ 语句中等待任何内容,因此您无法处理结果或等待其他内容。

    如果没有 async/await,在 LINQ 语句中您只是开始任务,而不是等待它们完成。它们最终仍会完成,但它会在控件离开 LINQ 语句后很久才会发生,因此您只能在 WhenAll 行完成后访问它们的结果,但不能在 LINQ 语句内访问。

    【讨论】:

      【解决方案2】:

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

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

      Task BazAsync(TBar bar);
      

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

      IEnumerable<Task> tasks = bars.Select(bar => BazAsync(bar));
      

      同样,如果您在委托中使用 asyncawait,您将创建一个返回 Task 的异步委托:

      IEnumerable<Task> tasks = bars.Select(async bar => await BazAsync(bar));
      

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

      就像常规的 LINQ 表达式一样,IEnumerable&lt;Task&gt; 是惰性求值的。只是,对于像 BazAsync 这样的异步方法,您通常想要意外的双重评估或类似的事情。因此,当您投影到一系列任务时,立即具体化该序列通常是一个好主意。这会为源序列中的所有元素调用BazAsync,开始所有任务:

      Task[] tasks = bars.Select(bar => BazAsync(bar)).ToArray();
      

      当然,我们对Select 所做的只是开始 对每个元素进行异步操作。如果您想等待它们全部完成,请使用Task.WhenAll

      await Task.WhenAll(tasks);
      

      大多数其他 LINQ 运算符不能像使用异步委托那样干净利落。 Select 非常简单:您只是为每个元素启动了一个异步操作。

      【讨论】:

      • “没有重要的区别。” - 如果 BazAsync 同步抛出,使用非异步 lambda 会在枚举期间发生,而使用异步 lambda 会在等待任务时发生。
      • @EliArbel:是的。但是同步抛出的异步方法是一种非常不寻常的情况。如果异步方法是boneheaded exceptions,则应该抛出同步异常。所以我不认为这是一个重要的用例。
      猜你喜欢
      • 2013-05-25
      • 1970-01-01
      • 2011-03-31
      • 2015-07-19
      • 2013-06-17
      • 2019-01-03
      • 2021-08-28
      • 1970-01-01
      • 2016-05-12
      相关资源
      最近更新 更多