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