异步任务在 for 循环中运行:System.ArgumentOutOfRangeException



我对编程相当陌生,决定测试我有一个想法,涉及异步执行数组排序任务。我几乎是异步编程的新手,并且遇到了一个错误,似乎只能通过某种奇怪的异步来解释......东西。。。发生。

当我逐步完成以下代码进行调试时,它运行良好。但是,当我允许它自由运行时,我遇到了参数超出范围异常:我变得大于 allArrays.Count - 1,并且程序失败。然而,if (i >= allArrays.Count) Console.WriteLine($"i is too high! i = {i}");

行在失败之前永远不会执行。有人可以向我解释这一点,并帮助提出这个问题的解决方案吗?

谢谢!

//iterate while allArrays contains multiple arrays to be merged.
while (allArrays.Count > 1)
{
if (allArrays.Count % 2 != 0)//if it's not even, we add one and make it even, so every array has a partner!
{
allArrays.Add(new int[0]);
}
for (int i = 0; i < allArrays.Count - 1; i+=2)
{
Console.WriteLine($"i is {i}");
if (i >= allArrays.Count) Console.WriteLine($"i is too high! i = {i}");
mergeTasks.Add(Task.Run(() => Merge(allArrays[i], allArrays[i + 1])));
}
await Task.WhenAll(mergeTasks);
//empty the list of smaller arrays
allArrays.Clear();
//add results to all arrays then empty mergeTasks
mergeTasks.ForEach(r => allArrays.Add(r.Result));
mergeTasks.Clear();
}

添加临时变量来存储i并在 lambda 中将其用于任务:

for (int i = 0; i < allArrays.Count - 1; i+=2)
{
var tmp = i; // create copy of current i
Console.WriteLine($"i is {i}");
if (i >= allArrays.Count) Console.WriteLine($"i is too high! i = {i}");
// use tmp here instead of i: 
mergeTasks.Add(Task.Run(() => Merge(allArrays[tmp], allArrays[tmp  + 1]))); 
}

Task.Run接受 lambda。要使用外部范围的变量,它将创建所谓的闭包。对于for循环,使用相同的闭包实例,因此捕获的值可以更改,因此它可以在最后一个任务调用之前更改为最后一个值(即i > allArrays.Count - 1(,Merge(allArrays[i], allArrays[i + 1])导致问题中的异常。

您可以尝试"验证"此行为,例如:

mergeTasks.Add(Task.Run(() => {
if (i >= allArrays.Count) Console.WriteLine($"i is too high! i = {i}");
return Merge(allArrays[tmp], allArrays[tmp  + 1]);
}));

您可以通过本文或此问题更深入地了解。

最新更新