如何并行更新Dictionary条目?替代顺序迭代修改的可行性问询
如何并行修改C# Dictionary的条目
嘿,我来帮你搞定这个并行修改字典的问题!首先得指出你示例里的一个小坑:你写的顺序代码里用dict[i]是不对的哦,Dictionary<int, byte>的索引是key值,不是循环的顺序索引,直接这么写大概率会抛出KeyNotFoundException(除非你的key刚好是从0到Count-1的连续整数)。正确的顺序迭代修改应该是这样的:
Dictionary<int, byte> dict = new Dictionary<int, byte>(); // 先给字典填充一些测试数据 foreach (var key in dict.Keys.ToList()) // 用ToList避免遍历过程中修改集合引发的异常 { dict[key] = 255; }
好了,回到你关心的并行操作问题,咱们一步步说:
能不能直接并行修改普通Dictionary?
先给你明确答案:绝对不行!Dictionary<TKey, TValue>本身不是线程安全的,多个线程同时修改它(哪怕是改不同的key),很可能会导致数据错乱、集合内部结构损坏,甚至直接抛出异常——毕竟它的内部哈希表结构没考虑多线程场景。
那怎么安全实现并行修改?给你几个实用方案:
方案1:换成线程安全的ConcurrentDictionary
如果可以把原字典换成ConcurrentDictionary<TKey, TValue>,那并行操作就省心多了,它本身就是为多线程读写设计的。示例代码如下:
var concurrentDict = new ConcurrentDictionary<int, byte>(); // 先填充一些数据 Parallel.ForEach(concurrentDict.Keys, key => { // 这里可以加你的条件判断,比如 if (key % 2 == 0) // 直接用索引器赋值就行,ConcurrentDictionary的索引器是线程安全的 concurrentDict[key] = 255; });
方案2:给普通Dictionary加锁同步
如果不想换字典类型,那可以在并行遍历的时候,把修改字典的操作用锁包起来,避免多线程同时修改引发的问题。不过要注意,这种方式修改的时候还是串行的,但遍历和条件判断(如果有计算逻辑)可以并行,适合修改逻辑比较重的场景:
Dictionary<int, byte> dict = new Dictionary<int, byte>(); // 填充测试数据 var lockObj = new object(); // 专门用于锁的对象,不要直接锁dict Parallel.ForEach(dict.Keys.ToList(), key => { // 先做你的条件判断,比如 if (dict[key] < 100) lock (lockObj) { dict[key] = 255; } });
方案3:并行生成新字典(最推荐的无副作用方式)
如果你的修改逻辑是基于原字典的条目生成新值,我最推荐这种方式:并行处理所有符合条件的条目,直接生成一个新字典。这样完全没有线程安全问题,因为原字典只是被读取,不会被修改,并行效率也最高:
Dictionary<int, byte> dict = new Dictionary<int, byte>(); // 填充测试数据 var newDict = dict.AsParallel() .Where(kvp => /* 这里放你的条件,比如 kvp.Key % 2 == 0 */) .ToDictionary(kvp => kvp.Key, kvp => (byte)255); // 如果需要替换原字典,直接赋值就行 dict = newDict;
最后再提几个注意点
- 不管哪种方式,都别在无同步的情况下让多线程碰普通的
Dictionary,哪怕你觉得只是改不同的key,字典内部的哈希表扩容、重组操作还是会出问题。 - 如果你的修改逻辑有依赖(比如改A key的值需要用到B key的最新值),那并行操作肯定会出问题,这种场景老老实实顺序处理吧。
内容的提问来源于stack exchange,提问作者pookie
相关产品推荐
相关产品推荐

