在本文中,我们将探讨在循环中使用关键字的正确场景以及何时避免使用它。
循环遍历特定方法,直到满足特定条件。尽管在循环中使用可能看起来很吸引人,但如果等待的方法可以并发运行,而忽略循环的固有特性,则可能会产生不利影响。有关异步编程的更多信息,请参阅我们的文章:如何异步执行多个任务。await
通过在我们的循环中使用,我们暂停迭代以允许等待的方法执行,然后继续进行下一次迭代。这意味着我们正在同步执行循环内部的操作。
public static async Task<List<int>> ResultAsync(int delayMilliseconds = 1000)
{
var numbers = new List<int> { 10, 20, 30, 40, 50, 60, 70, 80, 90 }; var result = new List<int>();
foreach (var number in numbers)
{
Console.WriteLine($"Processing {number}");
await Task.Delay(delayMilliseconds);
Console.WriteLine($"Processed {number}");
result.Add(number);
}
Console.WriteLine("Done Processing");
return result;
}
在这里,我们添加了方法。这意味着我们必须等待完成,然后才能继续进行下一次迭代。
在循环中使用可确保元素按顺序执行。但是,这也意味着完成整个迭代可能需要很长时间。
现在,让我们看一下控制台输出:
Processing 10
Processed 10
Processing 20
Processed 20
...
Done Processing
我们观察到,该程序按照列表中提供的顺序正确处理数字。
既然我们知道在循环中使用可以使其同步,那么我们可以探索如何使我们的循环异步。这是使用 .简单来说,它允许我们等待多个方法同时完成,并在所有方法完成后返回已完成的任务。
public static async Task<List<Task>> ResultAsync(int delayMilliseconds = 1000)
{
var numbers = new List<int> { 10, 20, 30, 40, 50, 60, 70, 80, 90 }; var result = new List<Task>();
foreach (int number in numbers)
{
result.Add(ProcessNumberAsync(number, delayMilliseconds));
}
await Task.WhenAll(result);
Console.WriteLine("Done Processing");
return result;
}
public static async Task ProcessNumberAsync(int number, int delayMilliseconds)
{
Console.WriteLine($"Processing {number}");
// Simulate an asynchronous operation
await Task.Delay(delayMilliseconds);
Console.WriteLine($"Processed {number}");
}
在这里,我们没有在循环中使用。我们只需遍历每个数字,为每个数字启动一个任务,然后将它们添加到列表中。然后,我们将列表传递给 。在循环之外,我们只需要等待完成,然后返回结果。
比较我们的两个示例方法,不使用循环执行速度更快。
我们还应该注意,与我们的同步不同,任务的执行顺序不能得到保证,因为每个任务都是异步计算的。
让我们再看一下控制台输出:
Processing 10
Processing 20
...
Processing 90
Processed 90
...
Done Processing
正如预期的那样,输出显示数字是以不确定的顺序处理的。
Task.WhenAll()使跟踪执行顺序变得困难,因为操作可能会并行完成。另一方面,使用 in 循环允许我们跟踪执行顺序,但可能会导致性能下降。
当多个任务独立运行时,使用是有益的。这意味着任务的执行不依赖于前一个任务的结果。
在这两种方法之间的选择取决于我们的具体目标。
当需要特定的执行序列时,我们在循环中使用。相反,当执行顺序不重要时,使用旨在提高性能。