【问题标题】:await not awaiting when used in a LINQ expression inside a List<>.ForEach method call在 List<>.ForEach 方法调用中的 LINQ 表达式中使用时不等待
【发布时间】:2020-05-08 12:44:43
【问题描述】:

我有一个异步函数,我必须对列表的每个元素进行异步调用。为此,我编写了这段代码:

List<string> batchItems;

batchItems.ForEach(async t => await SubmitBatchItemAsync(input, t));

但是,这不起作用:调用了 SubmitBatchItemAsync,但没有等待。

我必须将此代码更改为此才能正常工作:

List<string> batchItems;
foreach (var batchItem in batchItems)
    {
        await SubmitBatchItemAsync(input, batchItem);
    }

这也有效,但并不完全相同,因为 Task.Wait() 的工作方式与 await 不完全相同:

List<string> batchItems;

batchItems.ForEach(t => SubmitBatchItemAsync(input, t).Wait(CancellationToken.None));

有谁知道为什么第一个选项不起作用,因为 LINQ 表达式支持等待? (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/lambda-expressions#async-lambdas)

【问题讨论】:

标签: c# foreach async-await wait


【解决方案1】:

ForEach 不知道async。只需一个Action 即可对可枚举的每一项执行。

所以这意味着您的 lambda 表达式 async t =&gt; await SubmitBatchItemAsync(input, t) 没有 像通常那样被转换为 Func&lt;T, Task&gt;;它正在被转换为Action&lt;T&gt;,强制它为async void

pitfalls of async void methods 之一是调用代码无法轻易确定它们何时完成。

TL;DR:有些 API 不支持async-aware,在处理异步代码时应该避免使用。 ForEach 是这些 API 之一。

【讨论】:

    【解决方案2】:

    您可能会发现此扩展方法对Lists 有用:

    public static async Task ForEachAsync<T>(this List<T> source, Func<T, Task> function)
    {
        foreach (var item in source)
        {
            await function(item);
        }
    }
    

    使用示例:

    await batchItems.ForEachAsync(async t => await SubmitBatchItemAsync(input, t));
    

    TBH 这并不像看起来那么有用,因为在处理异步操作时,您通常希望以某种程度的并发来启动它们,而不是在另一个完成之后。

    【讨论】:

      猜你喜欢
      • 2023-02-03
      • 2022-11-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-01
      • 1970-01-01
      • 2021-09-28
      相关资源
      最近更新 更多