我有两个看似相同的循环的实现:
List<Type> types = ...
tasks = new Task<List<TypeInfo>>[types.Count];
for (Int32 i = 0; i < types.Count; i++)
{
tasks[i] = Task<List<TypeInfo>>.Run(() => GetBaseTypeList(types[i]));
}
Task.WaitAll(tasks);
当我这样做时,可能会发生IndexOutOfRangeException
,因为异步任务启动时,i
现在可以有任何值。
到目前为止,我确实理解它背后的机制。
我不明白的是,为什么会这样:
List<Type> types = ...
tasks = new Task<List<TypeInfo>>[types.Count];
for (Int32 i = 0; i < types.Count; i++)
{
Type t;
//do not put the indexer types[i] directly into the call because when the task is executed, i can have any value (delayed execution!).
t = types[i];
tasks[i] = Task<List<TypeInfo>>.Run(() => GetBaseTypeList(t));
}
Task.WaitAll(tasks);
t
在每个循环期间也会发生变化,但为什么所有任务都完美地执行了在循环迭代期间分配的t
?
答案 0 :(得分:4)
t
在循环过程中没有改变。
t
是for
循环范围内的局部变量,这基本上意味着它是一个&#34; new&#34;每次迭代t
。
i
贯穿for
循环的所有迭代(否则每次迭代将为0),超出这个范围,这就是为什么它在每次迭代中都会发生变化而且不应该# 39;关闭。
例如,在Type t;
t
的值为null
之后的第二次迭代中,而不是上一次迭代中的类型。
答案 1 :(得分:1)
你必须了解闭包。 即使它如何委托捕获循环变量也有区别。
当你第一次循环执行时,任务可能等待执行,到那时i的值增加或者可能是循环结束并等待所有任务完成。在那时,如果任务运行,那么它会尝试执行类型[i],在某些情况下,它会在索引中显而易见。
for (Int32 i = 0; i < types.Count; i++)
{
tasks[i] = Task<List<TypeInfo>>.Run(() => GetBaseTypeList(types[i]));
}
对于你的第二个循环,你首先将它复制到循环的局部变量并且被捕获,这样当任务运行时它不必依赖于i的值。
您可以在此处找到更多信息。 http://csharpindepth.com/articles/chapter5/closures.aspx