C# For循环超出范围 使用系统; 使用System.Collections.Generic; 使用系统文本; 使用System.Threading.Tasks; 命名空间控制台应用程序1 { 班级计划 { 静态void Main(字符串[]参数) { MyClass MyClass=新的MyClass(); myClass.StartTasks(); } } 类MyClass { int[]arr; 公共无效StartTasks() { arr=新整数[2]; arr[0]=100; arr[1]=101; 对于(int i=0;iWorkerMethod(arr[i]);//IndexOutOfRangeException:i==2!!! } } 无效工作方法(int i) { } } }

C# For循环超出范围 使用系统; 使用System.Collections.Generic; 使用系统文本; 使用System.Threading.Tasks; 命名空间控制台应用程序1 { 班级计划 { 静态void Main(字符串[]参数) { MyClass MyClass=新的MyClass(); myClass.StartTasks(); } } 类MyClass { int[]arr; 公共无效StartTasks() { arr=新整数[2]; arr[0]=100; arr[1]=101; 对于(int i=0;iWorkerMethod(arr[i]);//IndexOutOfRangeException:i==2!!! } } 无效工作方法(int i) { } } },c#,for-loop,C#,For Loop,在循环迭代完成之前,i++似乎又执行了一次。为什么要获取IndexOutOfRangeException?原因是在并行任务中使用循环变量。因为任务可以并发执行,所以循环变量的值可能与启动任务时的值不同 您在循环中启动了任务。当任务开始查询循环变量时,循环已经结束,因为变量i现在已超出停止点 即: i=2,循环退出 任务使用变量i(现在是2) 应该使用Parallel.For并行执行循环体。这是 另外,如果您想保持当前结构,可以将i的一个副本复制到一个循环局部变量中,循环局部副本将在并行任务中

在循环迭代完成之前,i++似乎又执行了一次。为什么要获取IndexOutOfRangeException?

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

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

即:

  • i=2,循环退出
  • 任务使用变量i(现在是2)
应该使用Parallel.For并行执行循环体。这是

另外,如果您想保持当前结构,可以将i的一个副本复制到一个循环局部变量中,循环局部副本将在并行任务中保持其值

e、 g

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

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

当您使用闭包时,重要的是要理解,您现在没有使用变量的值,而是使用变量本身。因此,如果在循环中创建lambda,如下所示:

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

这是尝试的另一个原因-它提供了许多警告,帮助您尽早捕获此类错误。“结束循环变量”就是其中之一,使用
foreach
不会抛出:

for (int i = 0; i < 2; i++)
{
    int index = i;
    Task.Factory.StartNew(() => WorkerMethod(arr[index])); 
}
但它也不起作用:

foreach (var i in arr)
{
  Task.Factory.StartNew(() => WorkerMethod(i));
}
它使用数组中的最后一个条目执行
WorkerMethod
。其他答案很好地解释了原因

这确实有效:


这实际上是我第一次亲身体验
System.Threading.Tasks
。我发现这个问题,我天真的回答,尤其是其他一些答案对我的个人学习经验很有用。我将把我的答案留在这里,因为它可能对其他人有用。

这里还有一些。还有一个很好的大型并行程序,来自MS;小问题:为什么
foreach
不抛出?纯粹的机会?@Marijn:看看我对你在
foreach
上的回答的评论,不抛出例外听起来像是一个经典的结束问题+1:这很有趣!很好的代码,复制粘贴,我可以自己测试。很明显,微软也注意到了,这种行为很愚蠢,他们修正了它。在C#5.0中,上述代码将按预期工作:-DSo很高兴以前有人严重遇到过此问题。。。。我只是觉得我很愚蠢,因为我的for循环超出了范围,哈哈,投了赞成票。这是一个非常有用的问题。它之所以有效,是因为它里面没有
arr[i]
。当
WorkerMethod
尝试访问
arr[2]
值时,会出现异常。请再次查看
WorkerMethod()
接收到的值。所有任务都将收到集合的最后一个值。它会起作用(就像不扔一样),但不会像预期的那样起作用。@Dyppl-事实上,正如杰夫所指出的,它甚至不起作用。但是它并没有抛出-hurray:-(+1这个答案对于实际发生的事情(闭包可能/在循环完成后进行评估)比声明“…因为任务可以并发执行,所以循环变量的值可能与您启动任务时的值不同”更清楚。”@瓶颈:在这种情况下,它仍然与并发性有关,因为
StartNew
实际上就在那里开始了任务,所以闭包可能会及时计算。如果你填充
Sleep(1000)
在循环内部,您可能会没事。但我仍然认为这是一个误解闭包而不是并发+1的问题;对于无耻的插件,这一点也不重要:R#确实警告过“访问修改的闭包”。请注意,在出现
for
foreach
循环时,它会发出警告。在阅读了大量MSDN文档后,它似乎不是一个bug,而是一个特性。整个lambda表达式在循环退出后执行,这很混乱,可能会产生如上所述的结果。我完全不想使用lambda,但MSDN每天都会使用它现在在示例中的何处。是否有其他方法可以使用任意输入(例如int、string和char)启动线程不必创建单独的类并将其作为对象传递?我希望尽可能避免运行时类型检查。@user782534:这不是lambda表达式的整体问题,而是使用闭包的问题。闭包允许您使用声明lambda表达式的上下文中的变量。这通常非常方便,y你只需要小心。Lambda表达式和闭包的存在正是因为这个原因:因为所有其他方法都需要你编写大量的管道,比如创建一个类等。与其避免Lambda和闭包,不如确保你完全理解它,让它成为你的朋友。这是一篇好文章。
for (int i = 0; i < 2; i++)
{
    int index = i;
    Task.Factory.StartNew(() => WorkerMethod(arr[index])); 
}
foreach (var i in arr)
{
  Task.Factory.StartNew(() => WorkerMethod(i));
}
101
101
Parallel.ForEach(arr, 
                 item => Task.Factory.StartNew(() => WorkerMethod(item))
                 );