.NET-字典锁与 ConcurrentDictionary

我找不到关于 ConcurrentDictionary类型的足够信息,所以我想在这里问一下。

目前,我使用一个 Dictionary来保存多个线程不断访问的所有用户(来自一个线程池,因此没有确切的线程数量) ,并且它具有同步访问。

我最近发现,在。NET 4.0,看起来非常令人愉快。我想知道,什么是“更有效和更容易管理”的选项,因为我有一个选项之间有一个正常的 Dictionary与同步访问,或有一个 ConcurrentDictionary已经是线程安全的。

参考.NET 4.0的 ConcurrentDictionary

75545 次浏览

基本上,您希望使用新的 ConcurrentDictionary。开箱即用,你必须写更少的代码,使线程安全的程序。

在使用线程安全集合时仍然需要非常小心,因为线程安全并不意味着可以忽略所有线程问题。当一个集合宣称自己是线程安全的时候,它通常意味着即使多个线程同时读写,它也保持一致的状态。但这并不意味着单个线程在调用多个方法时会看到一个“逻辑”结果序列。

例如,如果您首先检查一个键是否存在,然后获得与该键对应的值,那么即使使用 ConcurrentDictionary 版本,该键也可能不再存在(因为另一个线程可能已经删除了该键)。在这种情况下,您仍然需要使用锁定(或者更好的做法是: 使用 TryGetValue组合这两个调用)。

因此,一定要使用它们,但是不要认为它给了您忽略所有并发性问题的自由通行证。你还是要小心。

我认为 ConcurrentDictionary. GetOrAdd 方法正是大多数多线程场景所需要的。

线程安全的集合与非线程安全的集合可以用不同的方式来看待。

考虑一下没有店员的商店,除了收银台。如果人们不负责任,你会有很多问题。例如,假设一个顾客从金字塔易拉罐中取出一个罐子,而一个职员正在建造金字塔,那么一切都会变得一团糟。或者,如果两个顾客在同一时间达到同一项目,谁赢了?会打起来吗?这是一个非线程安全的集合。有很多方法可以避免问题,但是它们都需要某种锁定,或者以某种方式显式访问。

另一方面,假设一家商店有一个书桌前的店员,你只能通过他购物。你排队,问他要东西,他把东西还给你,你就离开队伍。如果你需要多件物品,你只能在每次往返的时候尽可能多地取你能记住的物品,但是你要小心避免占用店员的时间,这会激怒排在你后面的其他顾客。

现在想想这个。在只有一个店员的商店里,如果你一直排到队伍的最前面,问店员“你有卫生纸吗”,他说“有”,然后你说“好的,我知道我需要多少就给你回电话”,那么当你回到队伍的最前面时,商店当然就卖光了。线程安全集合不会阻止此场景。

线程安全集合保证其内部数据结构在任何时候都是有效的,即使是从多个线程访问的。

非线程安全的集合没有任何这样的保证。例如,如果你在一个线程上向一个二叉树添加了一些东西,而另一个线程正忙于重新平衡该树,不能保证该项会被添加,甚至不能保证之后该树仍然有效,那么它可能会被破坏得无可救药。

然而,线程安全集合并不能保证线程上的顺序操作都在其内部数据结构的相同“快照”上工作,这意味着如果您有如下代码:

if (tree.Count > 0)
Debug.WriteLine(tree.First().ToString());

您可能会得到一个 NullReferenceException,因为在 tree.Counttree.First()之间,另一个线程已经清除了树中剩余的节点,这意味着 First()将返回 null

对于这个场景,您要么需要查看所涉及的集合是否有安全的方法来获得您想要的东西,也许您需要重写上面的代码,或者您可能需要锁定。

你有没有看过。净值3.5 sp1。根据 Jon Skeet 的说法,他们已经向后移植了一系列并行扩展和并发数据结构。Net3.5 sp1.

有一套样品。Net 4 Beta 2,它非常详细地描述了如何使用它们的并行扩展。

我上周刚刚用32个线程测试了 ConcurrentDictionary 来执行 I/O。它似乎像宣传的那样工作,这表明已经对它进行了大量的测试。

编辑 : . NET 4 ConcurrentDictionary 和模式。

微软发布了一个名为“并行编程模式”的 pdf 文件。它真的值得下载,因为它描述了非常漂亮的细节正确的模式使用。Net 4并发扩展和要避免的反模式。就是这个。

内部 ConcurrentDictionary 对每个散列桶使用单独的锁。只要您只使用 Add/TryGetValue 和类似的方法来处理单个条目,字典将作为一个几乎没有锁的数据结构工作,并且具有各自的性能优势。OTOH 枚举方法(包括 Count 属性)一次锁定所有的 bucket,因此在性能方面比同步 Dictionary 差。

要我说,就用 ConcurrentDictionary 吧。

我们对缓存的集合使用了 ConcurrentDictionary,它每1小时重新填充一次,然后由多个客户端线程读取,类似于 这个示例线程安全吗?问题的 解决方案

我们发现,将其改为 只读字典可以提高整体性能。