什么时候应该使用ConcurrentDictionary和Dictionary?

时间:2017-02-02 22:08:37

标签: c# dictionary concurrentdictionary

我总是对选择哪一个感到困惑。据我所知,如果我想要两种数据类型DictionaryList,我会Key使用Value,因此我可以通过key轻松找到值但如果我应该使用ConcurrentDictionaryDictionary

,我总是感到困惑

在我离开之前没有对此进行太多研究之前我曾尝试过,但似乎谷歌在DictionaryConcurrentDictionary之间没有任何关系,但每个人都有一些东西一个人。

之前我曾经问过这样的朋友,但他们说的只是:"如果你在代码中使用了很多字典,那就使用ConcurrentDictionary"而我真的不想纠缠他们更详细地解释它。任何人都可以扩展这个吗?

5 个答案:

答案 0 :(得分:40)

"如果您在代码中使用了很多字典,请使用ConcurrentDictionary"是一种含糊的建议。我不会因为混乱而责备你。

ConcurrentDictionary主要用于您从多个线程(或异步任务)更新字典的环境中。如果它来自单个线程,您可以使用标准Dictionary代码;)

如果您查看ConcurrentDictionary上的方法,您会发现一些有趣的方法,例如TryAddTryGetValueTryUpdateTryRemove

例如,考虑一下您使用普通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是最佳选择的方案数量实际上很少。这样做有两个原因:

  1. ConcurrentDictionary提供的线程安全保证仅限于对其内部状态的保护。而已。如果您想做一些微不足道的事情,例如将字典更新为一个原子操作,那么您很不走运。 ConcurrentDictionary不支持这种情况。甚至不保护它包含的元素(如果它们是可变对象)。如果尝试使用AddOrUpdate方法更新其值之一,则字典将受到保护,但该值将不受保护。在这种情况下,Update意味着将现有值替换为另一个,而不是修改现有值

  2. 每当您想使用ConcurrentDictionary时,通常都有更好的选择。不涉及共享状态的替代方案,本质上就是ConcurrentDictionary。无论其锁定方案有多高效,它都将很难击败一个根本没有共享状态的体系结构,并且每个线程在不干扰其他线程的情况下做自己的事情。遵循此原理的常用库是PLINQTPL Dataflow库。下面是一个PLINQ示例:

Dictionary<string, Product> dictionary = productIDs
    .AsParallel()
    .Select(id => GetProduct(id))
    .ToDictionary(product => product.Barcode);

您可以信任PLINQ来使用更高效的策略来生成字典,而不是先创建字典,然后将多个线程同时用值填充它,这涉及对初始工作负载进行分区,并将每个分区分配给不同的工作线程。一个线程最终将汇总部分结果,并填充字典。