.NET – 字典locking与ConcurrentDictionary

我无法find关于ConcurrentDictionarytypes的足够信息,所以我想我会在这里问一下。

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

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

参考.NET 4.0的ConcurrentDictionary

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

考虑一个商店没有店员,除了在结帐。 如果人们不采取负责任的态度,你会遇到很多问题。 例如,假设一个顾客正在build造金字塔,一个顾客从一个金字塔jar头中取出一个jar头,所有的地狱都会破裂。 或者,如果两个客户同时到达同一个项目,谁赢了? 会有战斗吗? 这是一个非线程安全的集合。 有很多方法可以避免问题,但是它们都需要某种locking,或者以某种方式显式访问。

另一方面,考虑在办公桌上有店员的商店,你只能通过他购物。 你排队,向他索要一件物品,他把它带回给你,你就出去了。 如果您需要多件物品,您只能在每次往返中随身携带尽可能多的物品,但是您需要小心避免占用店员,这会激怒其他客户。

现在考虑这个。 在一个店员的店里,如果你一路走到行的前面,问店员“你有没有卫生纸”,他说“是”,然后你走“好吧,当我知道我需要多less东西的时候,我会回到你身边的。“那么当你回到线路的前面的时候,商店当然可以被卖掉。 线程安全集合不阻止这种情况。

线程安全集合保证其内部数据结构始终有效,即使从多个线程访问。

一个非线程安全的集合不附带任何这样的保证。 例如,如果你在一个线程上向二叉树添加了一些东西,而另一个线程忙于重新平衡这个树,那么不能保证这个东西会被添加,甚至之后这个树仍然是有效的,这可能是超出希望的。

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

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

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

对于这种情况,您需要查看所讨论的集合是否有安全的方法来获得您想要的内容,也许您需要重写上面的代码,或者您可能需要locking。

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

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

所以请使用它们,但不要认为它会给你一个自由的通行证来忽略所有的并发问题。 你仍然需要小心。

内部ConcurrentDictionary为每个散列桶使用一个单独的锁。 只要您只使用Add / TryGetValue等单个条目工作的方法,字典将作为一个几乎无锁的数据结构,各自的甜蜜的性能优势。 OTOH枚举方法(包括Count属性)会立即locking所有存储桶,因此性能上比同步字典更差。

我会说,只是使用ConcurrentDictionary。

你有没有看到.Net 3.5sp1的Reactive Extensions 。 根据Jon Skeet的说法,他们为.Net3.5 sp1提供了一系列并行扩展和并发数据结构。

.Net 4 Beta 2有一组样本,其中详细描述了如何使用并行扩展。

我刚刚花了上个星期用32个线程来testingConcurrentDictionary来执行I / O。 它似乎像广告一样工作,这将表明已经投入了大量的testing。

编辑 :.NET 4 ConcurrentDictionary和模式。

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

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

基本上你想用新的ConcurrentDictionary去。 开箱即用,您必须编写更less的代码才能生成线程安全的程序。

我们使用了ConcurrentDictionary作为caching集合,每隔1小时重新填充一次,然后通过多个客户端线程读取,类似于这个例子中线程安全 的解决scheme 。 题。

我们发现,把它改为ReadOnlyDictionary提高了整体性能。

有关c#: Pluralsight并发集合中的并发集合的很好的课程