为什么值类型和引用类型在具有任务的循环中表现不同?

时间:2014-12-11 16:39:12

标签: c# .net multithreading task-parallel-library task

我有两个看似相同的循环的实现:

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

2 个答案:

答案 0 :(得分:4)

t在循环过程中没有改变。

tfor循环范围内的局部变量,这基本上意味着它是一个&#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