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

Ash*_*kru 38 c# dictionary concurrentdictionary

我总是对选择哪一个感到困惑.当我看到它,我用DictionaryList,如果我想两个数据类型为KeyValue这样我就可以很容易地找到它的价值key,但我总是困惑,如果我要使用一个ConcurrentDictionaryDictionary

在我离开之前没有对此进行太多研究之前我已经尝试过,但似乎谷歌并没有真正得到任何关于Dictionaryvs ConcurrentDictionary但是每个人都有一些东西.

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

Jef*_*f B 47

"如果你在代码中大量使用你的字典,请使用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);
Run Code Online (Sandbox Code Playgroud)

这有一个问题,在检查它是否包含密钥和调用Add不同的线程之间可以调用Addid.当这个线程调用时Add,它会抛出异常.该方法TryAdd为您处理,并将返回一个true/false,告诉您是否添加了它(或者该键是否已经在字典中).

因此,除非您在多线程代码段中工作,否则您可能只需使用标准Dictionary类.话虽这么说,理论上你可以有锁来​​防止并发访问字典; 该问题已在"字典锁定与ConcurrentDictionary"中解决.

  • 谢谢你的回答,它帮助了很多。即使它来自单个线程,在任何地方使用 ConcurrentDictionary 也会如此糟糕吗? (2认同)
  • 你是否可以使用非并发形式,如果你在创建线程之前进行写入(一个多线程,只做读取)?我想这会更快,并且不能不知道它是如何破坏的(直到你做了一些愚蠢的事情,就像写它一样). (2认同)

And*_*rew 6

ConcurrentDictionary过度使用的最大原因Dictionary是线程安全。如果您的应用程序将同时使用同一个字典获得多个线程,则您需要线程安全ConcurrentDictionary,当这些线程写入或构建字典时尤其如此。

ConcurrentDictionary不使用多线程的缺点是开销。所有允许它是线程安全的函数仍然存在,所有的锁和检查仍然会发生,需要处理时间并使用额外的内存。

  • @AaronFranke Overhead,允许线程安全的功能仍然存在,所涉及的检查仍然会发生,因此它将是一个更大的对象并需要更多的处理来处理。其他阅读代码的人也会去寻找线程并可能被它弄糊涂。 (4认同)
  • 单线程代码的“ConcurrentDictionary”有什么缺点吗? (2认同)

The*_*ias 6

ConcurrentDictionary当您想要一个可以由多个线程同时安全访问的高性能字典时,A非常有用。Dictionary与使用 a 保护的标准相比lock,由于其细粒度锁定实现,它在大量使用时效率更高。不是所有线程都竞争单个锁,而是ConcurrentDictionary在内部维护多个锁,从而最大限度地减少争用,并限制成为瓶颈的可能性。

尽管有这些很好的特性,但使用 a 是最佳选择的场景ConcurrentDictionary实际上很少。原因有二:

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

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

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

您可以相信 PLINQ 会利用更高效的策略生成字典,包括对初始工作负载进行分区,并将每个分区分配给不同的工作线程,而不是预先创建字典,然后让多个线程同时向其中填充值。单个线程最终将聚合部分结果并填充字典。


Woo*_*193 5

ConcurrentDictionary当您需要跨多个线程(即多线程)访问字典时非常有用。VanillaDictionary对象不具备此功能,因此只能以单线程方式使用。

  • 你说不,然后继续说这是一件坏事,这是一件坏事吗?我问的原因是因为它目前无法从主线程以外的其他线程访问,但将来可能会。如果我决定实现多线程,是否值得只使用 Concurrent 来处理所有方式或更改我的代码? (2认同)
  • 我不知道你的具体需求是什么。我只是展示了使用它们的利弊。如果您认为将来可能需要对代码进行多线程处理,并且您的字典可能会跨多个线程使用,那么使用“ConcurrentDictionary”将是一个有效的设计选择。但是,如果您不打算这样做,那么您应该避免它,因为该数据结构不会最适合您的要求 (2认同)