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)
{
}
}
}
Run Code Online (Sandbox Code Playgroud)
似乎i ++在循环迭代完成之前再次执行.为什么我会得到IndexOutOfRangeException?
Col*_*kay 19
原因是您在并行任务中使用循环变量.由于任务可以同时执行,因此循环变量的值可能与启动任务时的值不同.
你在循环中启动了任务.当任务来查询循环变量时,循环已经结束,因为变量i现在超出了停止点.
那是:
您应该使用Parallel.For并行执行循环体.这是一个如何使用Parallel.For的示例
另外,如果你想维护当前的结构,你可以将i的副本复制到循环局部变量中,循环本地副本将其值保存到并行任务中.
例如
for (int i = 0; i < 2; i++)
{
int localIndex = i;
Task.Factory.StartNew(() => WorkerMethod(arr[localIndex]));
}
Run Code Online (Sandbox Code Playgroud)
Dyp*_*ppl 19
你正在关闭循环变量.当WorkerMethod被调用的时候,i可以得到值2,而不是值0或1.
当你使用闭包时,重要的是要理解你没有使用变量当前的值,你使用变量本身.所以如果你在循环中创建lambda就像这样:
for(int i = 0; i < 2; i++) {
actions[i] = () => { Console.WriteLine(i) };
}
Run Code Online (Sandbox Code Playgroud)
然后执行动作,它们都会打印"2",因为这就是目前的价值i.
在循环内引入局部变量将解决您的问题:
for (int i = 0; i < 2; i++)
{
int index = i;
Task.Factory.StartNew(() => WorkerMethod(arr[index]));
}
Run Code Online (Sandbox Code Playgroud)
<Resharper plug>这是尝试Resharper的另一个原因- 它提供了很多警告,可以帮助你及早发现像这样的bug."关闭一个循环变量"就在其中</ Resharper plug>