为什么值类型和引用类型在任务循环中的行为不同
本文关键字:循环 任务 类型 引用类型 为什么 | 更新日期: 2023-09-27 18:34:02
我有这两个看似相同的循环的实现:
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
完美执行?
t
在循环期间不会更改。
t
是 for
循环范围内的局部变量,这基本上意味着它是每次迭代的"新"t
。
另一方面,i
存在于 for
循环的所有迭代中(否则每次迭代将为 0),除此之外,这就是它在每次迭代中都会更改并且不应关闭的原因。
例如,在 Type t;
t
之后的第二次迭代中,值是 null
而不是上一次迭代中的类型。
你必须理解闭包。即使它与委托如何捕获循环变量之间也有差异。
当您第一次循环执行时,任务可能正在等待执行,到那时 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