本文介绍了如何处理ForEach()方法中的异步Lambda?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们遇到了产品中的错误,并将其减少到以下问题.给定一个带有异步lambda的列表并调用ForEach-Extension方法,输出的预期顺序是什么?

We came across a bug in our product and reduced it to the following problem.Given a list and call to the ForEach-Extension method with an async lambda, what is the expected order of the output:

public static async Task Main()
{
    var strings = new List<string> { "B", "C", "D" };
    Console.WriteLine("A");
    strings.ForEach(async s => { await AsyncMethod(s); } );
    Console.WriteLine("E");
}

private static async Task AsyncMethod(string s)
{
    await Task.Run(() => { Console.WriteLine(s); });
}

我们希望它始终是A,B,C,D,E.但有时是A,B,C,E,D或A,B,E,D,C

We expected it to be always A,B,C,D,E. But sometimes it's A,B,C,E,D or A,B,E,D,C

我们认为这两行是等效的:

We thought that these two lines would be equivalent:

strings.ForEach(async s => { await AsyncMethod(s); });

foreach (var s in strings) await AsyncMethod(s);

有人可以解释区别在哪里吗?这些异步lambda如何执行,为什么不等待它们呢?

Can somebody explain where the difference is? How are these async lambdas executed and why are they not awaited?

说明:问题不是B,C和D的顺序.问题是E在循环完成之前出现

Clarification: The problem is not the order of B,C and D. The problem is that E comes before the loop is finished

推荐答案

foreach (var s in strings) await AsyncMethod(s);

您误会了它的工作原理.这些是按顺序执行的步骤:

You're misunderstanding how this works. These are the steps that are taken, sequentially:

  1. 异步处理"B".
  2. 等待(1).
  3. 异步处理"C".
  4. 等待(3).
  5. 异步处理"D".
  6. 等待(5).

await 是每次迭代的一部分.下一次迭代要等到当前迭代完成后才能开始.

The await is part of each iteration. The next iteration won't start until the current one is finished.

由于不异步处理任务,因此这些顺序任务将按照它们开始的顺序来完成.

Due to not handling the tasks asynchronously, these sequential tasks will finish in the order that they were started.

strings.ForEach(async s => { await AsyncMethod(s); });

另一方面,它的工作方式却不同:

This, on the other hand, works differently:

  1. 异步处理"B".
  2. 异步处理"C".
  3. 异步处理"D".

ForEach 启动任务,但不会立即等待它们.由于异步处理的性质,每次运行代码时,这些并发任务可以以不同的顺序完成.

The ForEach starts the tasks, but does not immediately await them. Due to the nature of asynchronous handling, these concurrent tasks can be completed in a different order each time you run the code.

由于没有什么可以等待 ForEach 产生的任务,因此将立即启动"E"任务.BCDE都是异步处理的,可以按任意顺序完成.

As there is nothing that awaits the tasks that were spawned by the ForEach, the "E" task is started immediately. BCDE are all being handled asynchronously and can be completed in any arbitrary order.

您可以重新设计 foreach 示例以匹配您的 ForEach 示例:

You can redesign your foreach example to match your ForEach example:

foreach (var s in strings)
{
    AsyncMethod(s);
}

现在,处理方式与 ForEach 中的相同:

Now, the handling is the same as in the ForEach:

  1. 异步处理"B".
  2. 异步处理"C".
  3. 异步处理"D".


但是,如果要确保仅在完成BCD之后才启动E任务,则只需将BCD任务保存在一个集合中,即可将它们等待在一起:


However, if you want to ensure that the E task is only started when BCD have all been completed, you simply await the BCD tasks together by keeping them in a collection:

foreach (var s in strings)
{
    myTaskList.Add(AsyncMethod(s));
}

await Task.WhenAll(myTaskList);
  1. 异步处理"B"并将其任务添加到列表中.
  2. 异步处理"C"并将其任务添加到列表中.
  3. 异步处理"D"并将其任务添加到列表中.
  4. 请先执行列表中的所有任务,然后再执行其他操作.

这篇关于如何处理ForEach()方法中的异步Lambda?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-16 06:49