因为循环超出范围

时间:2011-06-03 10:39:53

标签: c# for-loop

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?

3 个答案:

答案 0 :(得分:19)

原因是您在并行任务中使用循环变量。由于任务可以同时执行,因此循环变量的值可能与启动任务时的值不同。

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

那是:

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

您应该使用Parallel.For并行执行循环体。这是an example of how to use Parallel.For

另外,如果你想维护当前的结构,可以将i的副本复制到循环局部变量中,循环本地副本将其值保存到并行任务中。

e.g。

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

答案 1 :(得分:18)

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

当你使用闭包时,重要的是要明白你没有使用变量当前的值,你使用变量本身。所以如果你在循环中创建lambda就像这样:

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])); 
}

&lt; Resharper plug&gt; 这是尝试Resharper的另一个原因 - 它提供了很多警告,可以帮助您及早发现像这样的错误。 “关闭循环变量”就在其中&lt; / Resharper plug&gt;

答案 2 :(得分:0)

使用foreach不会抛出:

foreach (var i in arr)
{
  Task.Factory.StartNew(() => WorkerMethod(i));
}

但是也不起作用:

101
101

它使用数组中的最后一个条目执行WorkerMethod。为什么在其他答案中得到了很好的解释。

工作:

Parallel.ForEach(arr, 
                 item => Task.Factory.StartNew(() => WorkerMethod(item))
                 );

注意

这实际上是我第一次使用System.Threading.Tasks的实践经验。我发现了这个问题,我的天真答案,特别是其他一些对我个人学习经历有用的答案。我会在这里留下我的答案,因为它可能对其他人有用。