为什么值类型和引用类型在任务循环中的行为不同

本文关键字:循环 任务 类型 引用类型 为什么 | 更新日期: 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

循环期间不会更改。

tfor 循环范围内的局部变量,这基本上意味着它是每次迭代的"新"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