因为循环超出范围

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  WorkerMethod(arr[i])); // IndexOutOfRangeException: i==2!!! } } void WorkerMethod(int i) { } } } 

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

你正在关闭循环变量。 当调用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])); } 

这是尝试Resharper的另一个原因 – 它提供了很多警告,可以帮助你及早发现像这样的bug。 “关闭一个循环变量”就在其中

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

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

那是:

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

您应该使用Parallel.For并行执行循环体。 这是一个如何使用Parallel.For的示例

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

例如

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

使用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实践经验。 我发现了这个问题,我天真的答案,特别是其他一些对我个人学习经历有用的答案。 我会在这里留下我的答案,因为它可能对其他人有用。