For循环超出范围

本文关键字:范围 循环 For | 更新日期: 2023-09-27 17:58:15

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass myClass = new MyClass();
            myClass.StartTasks();
        }
    }
    class MyClass
    {
        int[] arr;
        public void StartTasks()
        {
            arr = new int[2];
            arr[0] = 100;
            arr[1] = 101;
            for (int i = 0; i < 2; i++)
            {
                Task.Factory.StartNew(() => WorkerMethod(arr[i])); // IndexOutOfRangeException: i==2!!!
            }
        }
        void WorkerMethod(int i)
        {
        }
    }
}

似乎在循环迭代完成之前,i++又被执行了一次。为什么我会得到IndexOutOfRangeException?

For循环超出范围

您正在结束循环变量。当调用WorkerMethod时,i的值可以是2,而不是0或1。

当您使用闭包时,重要的是要理解您目前没有使用变量的值,而是使用变量本身。所以,如果你在循环中创建这样的lambdas:

for(int i = 0; i < 2; i++) {
    actions[i] = () => { Console.WriteLine(i) };
}

然后执行这些操作,它们都会打印"2",因为这就是i目前的值。

在循环中引入局部变量将解决您的问题:

for (int i = 0; i < 2; i++)
{
    int index = i;
    Task.Factory.StartNew(() => WorkerMethod(arr[index])); 
}

<Resharper插头>这是尝试Resharper的又一个原因——它给出了很多警告,帮助你尽早发现像这样的错误。"关闭循环变量"就是其中之一<Resharper插头>

原因是在并行任务中使用了循环变量。因为任务可以并发执行,所以循环变量的值可能与启动任务时的值不同。

您在循环中启动了任务。当任务开始查询循环变量时,循环已经结束,因为变量i现在已经超过了停止点。

即:

  • i=2并且循环退出
  • 任务使用变量i(现在为2(

您应该使用Parallel.For来并行执行循环体。以下是如何使用Parallel的示例。对于

或者,如果你想保持当前的结构,你可以将i的副本复制到循环局部变量中,循环局部副本将在并行任务中保持其值。

例如

for (int i = 0; i < 2; i++)
{
  int localIndex = i;
  Task.Factory.StartNew(() => WorkerMethod(arr[localIndex])); 
}