我总是对选择哪一个感到困惑。据我所知,如果我想要两种数据类型Dictionary
和List
,我会Key
使用Value
,因此我可以通过key
轻松找到值但如果我应该使用ConcurrentDictionary
或Dictionary
?
在我离开之前没有对此进行太多研究之前我曾尝试过,但似乎谷歌在Dictionary
与ConcurrentDictionary
之间没有任何关系,但每个人都有一些东西一个人。
之前我曾经问过这样的朋友,但他们说的只是:"如果你在代码中使用了很多字典,那就使用ConcurrentDictionary
"而我真的不想纠缠他们更详细地解释它。任何人都可以扩展这个吗?
答案 0 :(得分:40)
"如果您在代码中使用了很多字典,请使用ConcurrentDictionary"是一种含糊的建议。我不会因为混乱而责备你。
ConcurrentDictionary
主要用于您从多个线程(或异步任务)更新字典的环境中。如果它来自单个线程,您可以使用标准Dictionary
代码;)
如果您查看ConcurrentDictionary上的方法,您会发现一些有趣的方法,例如TryAdd
,TryGetValue
,TryUpdate
和TryRemove
。
例如,考虑一下您使用普通Dictionary
类时可能会看到的典型模式。
// There are better ways to do this... but we need an example ;)
if (!dictionary.ContainsKey(id))
dictionary.Add(id, value);
这有一个问题,在检查它是否包含密钥和调用Add
之间,另一个线程可以使用相同的Add
调用id
。当此线程调用Add
时,它会抛出异常。方法TryAdd
为您处理,并返回一个true / false,告诉您是否添加了它(或者该键是否已经在字典中)。
因此,除非您在多线程代码段中工作,否则您可以使用标准Dictionary
类。话虽这么说,理论上你可以有锁来防止并发访问字典;该问题已在"Dictionary locking vs. ConcurrentDictionary"中解决。
答案 1 :(得分:2)
使用ConcurrentDictionary
超过正常Dictionary
的最大原因是线程安全。如果您的应用程序将同时使用同一个字典获得多个线程,则需要线程安全的ConcurrentDictionary
,当这些线程正在写入或构建字典时尤其如此。
答案 2 :(得分:1)
ConcurrentDictionary
非常有用。 Vanilla Dictionary
对象不具备此功能,因此只能以单线程方式使用。
答案 3 :(得分:1)
上面接受的答案是正确的。然而,值得一提的是,如果一个字典没有被修改,即它只被读取,而不管线程数量如何,那么 Dictionary<TKey,TValue>
是首选,因为不需要同步。
例如在 Dictionary<TKey,TValue>
中缓存配置,该配置仅在启动时填充一次,并在应用程序的整个生命周期中使用。
When to use a thread-safe collection : ConcurrentDictionary vs. Dictionary
<块引用>如果您只读取键或值,Dictionary
答案 4 :(得分:-1)
ConcurrentDictionary
在需要高性能的字典时可以很有用,该字典可以被多个线程同时安全地访问。与受Dictionary
保护的标准lock
相比,由于其细化的锁定实现,它在大量使用时效率更高。 ConcurrentDictionary
而不是所有线程都在争夺单个锁,而是在内部维护多个锁,从而最大程度地减少了争用,并限制了成为瓶颈的可能性。
尽管具有这些不错的特性,但使用ConcurrentDictionary
是最佳选择的方案数量实际上很少。这样做有两个原因:
ConcurrentDictionary
提供的线程安全保证仅限于对其内部状态的保护。而已。如果您想做一些微不足道的事情,例如将字典和更新为一个原子操作,那么您很不走运。 ConcurrentDictionary
不支持这种情况。甚至不保护它包含的元素(如果它们是可变对象)。如果尝试使用AddOrUpdate
方法更新其值之一,则字典将受到保护,但该值将不受保护。在这种情况下,Update
意味着将现有值替换为另一个,而不是修改现有值。
每当您想使用ConcurrentDictionary
时,通常都有更好的选择。不涉及共享状态的替代方案,本质上就是ConcurrentDictionary
。无论其锁定方案有多高效,它都将很难击败一个根本没有共享状态的体系结构,并且每个线程在不干扰其他线程的情况下做自己的事情。遵循此原理的常用库是PLINQ和TPL Dataflow库。下面是一个PLINQ示例:
Dictionary<string, Product> dictionary = productIDs
.AsParallel()
.Select(id => GetProduct(id))
.ToDictionary(product => product.Barcode);
您可以信任PLINQ来使用更高效的策略来生成字典,而不是先创建字典,然后将多个线程同时用值填充它,这涉及对初始工作负载进行分区,并将每个分区分配给不同的工作线程。一个线程最终将汇总部分结果,并填充字典。