我想在我的应用程序中使用 ConcurrentDictionary,但首先我需要确保我正确理解它的工作原理。在我的应用程序中,我将有一个或多个线程写入字典或从字典中删除。而且,我会有一个或多个线程从字典中读取。可能同时发生。
我是否正确认为 ConcurrentDictionary 的实现会处理所有必需的锁定,而我不需要提供自己的锁定?换句话说,如果一个线程正在对字典进行写入或删除操作,那么读取线程(或另一个写入线程)将被阻塞,直到更新或删除完成为止?
非常感谢。
最佳答案
当前的实现混合使用了 strip 锁(我昨天在 https://stackoverflow.com/a/11950835/400547 对某人的回答中建议的技术)并非常非常努力思考操作不可能导致问题的情况或由并发操作引起的问题(其中有很多,但您必须非常确定是否使用它们)。
因此,如果您同时在并发字典上执行多项操作,则可能会出现以下情况:
- 甚至没有线程锁定,但一切正常。
- 一些线程锁定,但它们锁定在不同的事物上,并且不存在锁定争用。
- 一个或两个线程相互争用锁,速度变慢,但对性能的影响小于只有一个锁的情况。
- 一个或两个线程需要将整个事物锁定一段时间(通常用于内部调整大小),这会阻塞所有可能在上述情况 3 中被阻塞的线程,尽管有些线程可以继续运行(那些读取的线程)。<
这些都不涉及脏读,脏读只是与锁定相关的问题(我自己的并发字典形式根本不使用锁,它也没有脏读)。
此线程安全不适用于您的代码完成的批处理(如果您读取一个值然后写入一个值,则读取的值可能在您完成写入之前已更改),但请注意一些常见的情况需要 Dictionary
上的几个调用由 ConcurrentDictionary
上的单个方法满足(GetOrAdd
和 AddOrUpdate
做的事情将是两次调用 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
即可。
否则它会归结为可能有多少并发命中。如果您主要只打算让一个线程访问字典,但您需要防止并发访问的可能性,那么您绝对应该选择带锁的 Dictionary
。如果您将有六个或更多线程正在访问字典的时间段,那么您绝对应该选择 ConcurrentDictionary
(如果他们可能正在访问相同数量的键,那么请查看我的版本,因为那是我表现更好的一种情况。
很难说“少”线程和“多”线程之间的中间点在哪里。我会说,如果定期有两个以上的线程,则使用 ConcurrentDictionary
。如果不出意外,在项目的整个生命周期中,并发需求往往会增加而不是减少。
编辑:要回答您给出的特定情况,即一位作者和一位读者,根本不会有任何阻塞,因为出于与多个读者和一位作者在 Hashtable
上安全的大致相同的原因,这是安全的, 尽管 ConcurrentDictionary
在几个方面超越了这一点。
关于c# - ConcurrentDictionary 对象 - 通过不同的线程读写,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11969570/