字典<K,V>线程是否可以安全地进行同时读取和添加?

red*_*alx 6 .net dictionary ajaxcontroltoolkit thread-safety

这个问题涉及一个非常具体和常见的场景,其中字典被用于多线程环境中的项目的按需缓存.为了避免线程锁定,最好在同步锁之外测试现有的缓存项,但是如果我们随后必须添加一个项,那么它就算作对字典的写入,因此我在stackoverflow上读到的大多数建议都是你需要锁定读取和写入,因为调用add()可能会改变字典的内部状态.

但是,通过Microsoft的AjaxControlToolkit(scriptObjectBuilder类),代码确实在任何锁之外执行TryGet(),并且只锁定Add()新项到字典.我可以看到,如果放置项目的存储桶在添加后永远不会更改,这可能是怎么可能的,但我怀疑这是错误的,可能是错误的来源.

谢谢.

更新 通过.Net文档我认为所描述的模式确实是错误的.但是我想知道Dictionary的特定实现是否允许它以及AjaxControlToolkit是否依赖于此(这将是可疑的).在检查Reflector中的代码时我很确定这确实是错误的,Dictionary.Resize()方法重新分配桶的数量并移动桶项,因此TryGet()中间的任何线程都可能正在工作在不稳定的数据.

更新 已在codeplex上针对AjaxControlToolkit记录缺陷.看到:

Rex*_*x M 5

Tess Ferrandez有一篇关于通用词典线程问题的优秀博文:

FindEntry方法遍历字典,试图找到密钥.如果多个线程同时执行此操作,尤其是在此期间修改字典时,您可能最终在FindEntry中的无限循环中导致高CPU行为并且该进程可能会挂起.


red*_*alx -1

在进行一些投资后回答我自己的问题:在检查 Dictionary 的代码时,我可以看到 Dictionary.Resize() 方法重新分配用于存储数据的内部存储桶的数量,并重新分配存储桶内容,以便项目位于根据哈希码正确存储桶。因此,TryGet() 中间的任何线程都有处理不稳定数据的风险。

顺便说一句,Dictionary 类的一种可能的低锁定方法可能是仅在 Resize() 方法周围放置锁定。