C# · 12月 25, 2021

c# – ConcurrentDictionary对象 – 通过不同的线程读取和写入

我想在我的应用程序中使用ConcurrentDictionary,但首先我需要确保我能够正确理解它的工作原理.在我的应用程序中,我会有一个或多个线程写入或删除字典.而且,我会有一个或多个从字典读取的线程.潜在的,所有在同一时间.

我正确的说,ConcurrentDictionary的实现需要处理所有必需的锁定才能发生,我不需要提供我自己的锁定?换句话说,如果一个线程正在写入或从字典中删除,读取线程(或另一个写线程)将被阻止,直到更新或删除完成?

非常感谢.

解决方法 目前的实现使用条纹锁(我在昨天在 https://stackoverflow.com/a/11950835/400547的答案中提出的技术)的混合,并非常非常努力地讨论操作不可能导致并发操作引起问题或由此引起问题的情况(有很多这些,但你必须非常确定,如果你使用它们).

因此,如果您一次在并发字典中发生多个操作,则可以执行以下操作:

>没有线程甚至锁定,但一切正常.
有些线程锁定,但是它们锁定在单独的东西上,并且没有锁争用.
>一个或两个线程彼此具有锁争用,并且减慢速度,但是对于性能的影响小于单个锁的效果.
>一个或两个线程需要将整个内容锁定一段时间(通常用于内部调整大小),阻止所有在上述情况3中可能被阻止的线程,尽管有些可以继续(读取的那些).

这些都不涉及脏读,这只是一个与锁相关联的问题(我自己的并发字典形式根本不使用任何锁,也没有脏读).

此线程安全性不适用于您的代码完成的批次(如果您读取值然后写入值,读取的值可能在完成写入之前已更改),但请注意,一些常见的情况将需要一对通过ConcurrentDictionary(GetOrAdd和AddOrUpdate),通过单个方法调用Dictionary可以使用Dictionary进行两次调用,以便它们可以原子地完成 – 尽管请注意,涉及某些重载的Func可能会被多次调用).

由于这一点,ConcurrentDictionary没有增加危险,所以你应该选择如下:

如果你要锁定一些不符合ConcurrentDictionary提供的操作的批次,例如:

lock(lockObj){ var test = dict[key1]; var test2 = dict[key2]; if(test < test2 && test2 < dict[key3] && SomeOtherBooleanProducer()) dict[key4] = SomeFactoryCall(key4);}

那么你必须锁定ConcurrentDictionary,虽然可能有一种方法可以将它与支持并发的方式相结合,但是可能不会,所以只需使用带有锁的字典.

否则归结为可能会有多少并发点击.如果你大部分时间只有一个线程击中字典,但是你需要防范并发访问的可能性,那么你应该一定要用一个锁定去Dictionary.如果你打算有几十个线程击中字典,那么你一定要去找ConcurrentDictionary(如果他们很可能会打到相同的小数量的密钥,那么看看我的版本,因为那是我有更好表现的一种情况).

在“少数”和“许多”之间的中间位置在哪里,很难说.我会说,如果有两个以上的线程定期,然后去ConcurrentDictionary.如果没有别的话,来自并发的要求往往会比项目减少更频繁地在项目的整个生命周期内增加.

编辑:为了回答你给出的一个作者和一个读者的具体情况,根本就不会有任何阻止,因为对于多个读者和一个作者在Hashtable上是安全的,这是安全的,尽管ConcurrentDictionary在几个方面超越了这一点.