For 循环中的异步任务 运行:System.ArgumentOutOfRangeException

Asynchronous Task Run in a For Loop: 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) 16=]导致问题出现异常。

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

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

您可以通过 this article or this 问题进行更深入的研究。