因为循环超出范围
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])); }
原因是您在并行任务中使用循环变量。 由于任务可以并发执行,因此循环变量的值可能与启动任务时的值不同。
你在循环中启动了任务。 当任务来查询循环变量时,循环已经结束,因为变量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
实践经验。 我发现了这个问题,我天真的答案,特别是其他一些对我个人学习经历有用的答案。 我会在这里留下我的答案,因为它可能对其他人有用。