Linq ForEach,异步等待差异

时间:2018-07-06 16:02:29

标签: linq async-await

有人可以告诉我以下两种方法之间的区别,以及为什么会有不同的结果吗?我本以为方法2只是方法1的分解版本。我显然错过了一些关键的区别细节。什么事?

public interface IStrategy
{
    Task<List<Result>> ExecuteAsync();
}

public async Task Execute(List<Result> results, IEnumerable<IStrategy> strategies)
{
    // Approach 1, strategy will return 1 result, results collection will be empty at method exit
    strategies.ForEach(async strategy => results.AddRange(await strategy.ExecuteAsync()));

    // Approach 2, strategy will return 1 result, results collection will have one result at method exit
    foreach (var strategy in strategies)
    {
       results.AddRange(await strategy.ExecuteAsync());
    }
}

1 个答案:

答案 0 :(得分:3)

List<T>.Foreach()需要一个Action<T>。您尝试传递给ForEach的lambda由编译器转换为状态机,而在您await内部strategy.ExecuteAsync()调用时,该调用立即返回。

因此ForEach 认为尽管您的lambda实际上仍在等待strategy.ExecuteAsync(),但它已经完成。 ForEach无法(或指定)到await该方法(委托人甚至无法将Task返回到await)。
因此,它迭代到下一个strategy,而无需等待上一个ExecuteAsync()。最终ForEach返回,您不知道所有(或任何一个)策略是否已完全执行,它们可能仍在运行。

如果您在第一种方法返回后要等待一会儿,则results列表应正确填写(但是,这当然不是您应该这样做的方式!第二种方法是正确的方法。)


第二种方法有效,因为您总是await ExecuteAsync(),然后才继续进行迭代。因此,在foreach循环之后,所有策略都已执行。