You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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的枚举(比如拆分任务、遍历元素),这意味着:
    1. 并行任务中修改的只是临时生成的Person实例,这些实例在任务完成后就被丢弃了。
    2. 当你后续再去访问原IEnumerable时,又会重新执行Select逻辑,生成新的、未被修改的Person对象,自然看不到之前的修改效果。
  • 调用ToList()后,会立即执行Select逻辑,把所有Person实例一次性创建并存储到List集合中。此时IEnumerable指向的是List的枚举器,后续所有枚举操作都是访问同一个List中的固定实例。Parallel.ForEachAsync修改的就是这些实际存在于List中的实例,后续访问时自然能看到修改后的结果。

你调试时看到并行代码执行是事实,但修改的是每次枚举生成的临时对象,而非后续访问时的对象。Person是引用类型没错,但问题根源不在引用传递,而是IEnumerable延迟执行导致的实例重复创建。

内容的提问来源于stack exchange,提问作者user19291301

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 01:52:34