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

批量修改集合对象属性的实现方案、性能对比及并发集合选型咨询

批量修改集合对象属性的实现方案、性能对比及并发集合选型咨询

你好!针对你提出的「快速修改集合中对象属性」的需求,我整理了不同实现方案的代码、性能测试结果,以及相关问题的分析,一起来看看吧:

测试代码实现

我用BenchmarkDotNet做了五种不同实现方式的性能测试,完整代码如下:

using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public delegate void PtEventHandler(object source, EventArgs e);

public class Pt
{
    public string Nr { get; set; }
    public int X { get; set; }
    public int Y { get; set; }
    public bool Selected { get; set; }

    public void OnPtChanged(object source, EventArgs e)
    {
        Selected = true;
    }
}

public class Tests
{
    private Dictionary<string, Pt> dict = new Dictionary<string, Pt>();
    public event PtEventHandler PtChanged;

    [GlobalSetup]
    public void Setup()
    {
        for (int i = 0; i < 10000; i++)
        {
            var p = new Pt 
            { 
                Nr = i.ToString(), 
                X = i, 
                Y = i, 
                Selected = false
            };
            PtChanged += p.OnPtChanged;
            dict.Add(i.ToString(), p);
        }
    }

    [Benchmark]
    public void Linq()
    {
        dict.Values.ToList().ForEach(p => p.Selected = true);
    }

    [Benchmark]
    public void Loop()
    {
        for (int i = 0; i < 10000; i++) 
            dict[i.ToString()].Selected = true;
    }

    [Benchmark]
    public void Event()
    {
        PtChanged(this, EventArgs.Empty);
    }

    [Benchmark]
    public void ParallelLinq()
    {
        dict.Values.AsParallel().ForAll(p => p.Selected = true);
    }

    [Benchmark]
    public void ParallelLoop()
    {
        Parallel.For(0, 10000, i => 
        { 
            dict[i.ToString()].Selected = true; 
        });
    }
}

// 测试入口
class Program
{
    static void Main()
    {
        var result = BenchmarkRunner.Run<Tests>();
        Console.ReadKey();
    }
}

性能测试结果

以下是10000个对象的测试数据(单位:微秒us,数值越小性能越好):

MethodMeanErrorStdDev
Linq61.59 us1.581 us4.662 us
Loop306.84 us6.123 us16.448 us
Event44.25 us0.871 us1.503 us
ParallelLinq355.78 us7.064 us17.723 us
ParallelLoop119.16 us0.977 us0.816 us

方案性能分析

  1. Event事件驱动:性能最优,耗时仅44us左右。原理是初始化时给每个对象绑定事件处理方法,触发事件时所有对象会同时执行属性修改,完全避免了遍历集合的开销。但要注意:这种方式只适合全量修改的场景,如果是修改选中对象,事件驱动就不好精准控制(硬要加判断的话,会抵消大部分性能优势)。

  2. Linq遍历:性能次之,61us左右。用ToList().ForEach的方式代码简洁,但内部做了一次ToList的内存拷贝,不过比普通循环快很多,适合追求代码简洁的全量修改场景。

  3. ParallelLoop并行循环:119us左右,比单线程Linq慢,但比普通单线程Loop快。不过并行带来的线程调度开销在小数据量下会很明显,只有当数据量特别大(比如百万级以上)时,并行的优势才会体现出来。

  4. 普通Loop循环:性能最差,300us+。核心瓶颈是每次都要通过i.ToString()作为Key去字典里查找对象,字符串拼接和字典查找的开销累加起来非常大,这种实现方式不推荐。

  5. ParallelLinq并行Linq:性能垫底,355us+。同样是并行调度的额外开销,加上PLinq本身的处理逻辑,小数据量下完全不划算,不建议使用。

关于并发集合的选型问题

你问的「能不能用AsParallel或Parallel配合普通Dictionary,还是必须用ConcurrentDictionary」,分两种场景来看:

  • 仅修改对象内部属性(不改动字典结构):像你测试里的场景,只是修改字典中已有对象的Selected属性(不增删字典的Key/Value),普通Dictionary是可以安全使用的。因为.NET的Dictionary在仅读取Key/Value、不修改集合结构的情况下,并行访问不会有线程安全问题。
  • 需要修改字典结构:如果你的并行操作涉及到字典的增删改(比如Add、Remove、Clear),那必须用ConcurrentDictionary。普通Dictionary没有做并发安全设计,多线程修改集合结构会导致字典损坏、数据丢失甚至抛出异常,而ConcurrentDictionary专门针对并发场景做了优化,能安全处理这类操作。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 06:52:58