为何Parallel.ForEachAsync无法修改IEnumerable元素?ToList可解决
问题:Parallel.ForEachAsync无法修改IEnumerable元素,ToList后却正常生效
代码重现
首先定义Person类:
class Person { public required string Name { get; set; } public required string Lastname { get; set; } public override string ToString() => $"{Name} - {Lastname}"; }
创建基于延迟加载的IEnumerable集合:
// 测试数据 IEnumerable<Person> persons = Enumerable.Range(0, 1000).Select(index => new Person() { Name = index.ToString(), Lastname = index.ToString() + "^" });
尝试用Parallel.ForEachAsync修改元素,发现无法生效:
await ChangeByParallel(persons); // 无法修改IEnumerable中的元素 static async Task ChangeByParallel(IEnumerable<Person> persons) { await Parallel.ForEachAsync(persons, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, async (person, token) => { person.Name = "Ermalo"; person.Lastname = "Magradze"; await Task.Delay(100); }); }
将IEnumerable转为List后,修改操作正常生效:
IEnumerable<Person> persons = Enumerable.Range(0, 1000).Select(index => new Person() { Name = index.ToString(), Lastname = index.ToString() + "^" }).ToList(); // 添加ToList,底层集合变为List await ChangeByParallel(persons); // 修改生效 static async Task ChangeByParallel(IEnumerable<Person> persons) { await Parallel.ForEachAsync(persons, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, async (person, token) => { person.Name = "Ermalo"; person.Lastname = "Magradze"; await Task.Delay(100); }); }
原因解析
核心问题出在IEnumerable的延迟执行特性和Parallel.ForEachAsync的枚举逻辑上:
- 直接使用
Enumerable.Range(...).Select(...)返回的IEnumerable是延迟计算的——每次调用枚举器(比如遍历、Parallel.ForEachAsync处理)时,都会重新执行Select中的逻辑,创建一批全新的Person实例。 Parallel.ForEachAsync在并行处理时,会多次触发IEnumerable的枚举(比如拆分任务、遍历元素),这意味着:- 并行任务中修改的只是临时生成的Person实例,这些实例在任务完成后就被丢弃了。
- 当你后续再去访问原IEnumerable时,又会重新执行Select逻辑,生成新的、未被修改的Person对象,自然看不到之前的修改效果。
- 调用
ToList()后,会立即执行Select逻辑,把所有Person实例一次性创建并存储到List集合中。此时IEnumerable指向的是List的枚举器,后续所有枚举操作都是访问同一个List中的固定实例。Parallel.ForEachAsync修改的就是这些实际存在于List中的实例,后续访问时自然能看到修改后的结果。
你调试时看到并行代码执行是事实,但修改的是每次枚举生成的临时对象,而非后续访问时的对象。Person是引用类型没错,但问题根源不在引用传递,而是IEnumerable延迟执行导致的实例重复创建。
内容的提问来源于stack exchange,提问作者user19291301
相关产品推荐
相关产品推荐

