都说ConcurrentDictionary<TKey, TValue>有陷阱

看这么几句解释(英文原帖):

private static void ConcurrentDictionary()
{
    var dict = new ConcurrentDictionary<int, string>();
    ThreadPool.QueueUserWorkItem(LongGetOrAdd(dict, 1));
    ThreadPool.QueueUserWorkItem(LongGetOrAdd(dict, 1));
}

private static WaitCallback LongGetOrAdd(ConcurrentDictionary<int, string> dict, int index)
{
    return o => dict.GetOrAdd(index,i =>
                                        {
                                            Console.WriteLine("Adding!");
                                            Thread.SpinWait(1000);

                                            return i.ToString();
                                        }
                    );
}

1) threadA calls GetOrAdd, finds no item and creates a new item to Add by invoking the valueFactory delegate.

线程A调用GetOrAdd,发现数据不存在并创建了一条新数据,准备调用委托方法添加数据

2) threadB calls GetOrAdd concurrently, its valueFactory delegate is invoked and it arrives at the internal lock before threadA, and so its new key-value pair is added to the dictionary.

线程B现在也在调用GetOrAdd方法,它的委托被调用了,比线程A先进入GetOrAdd内部的锁,因此它创建的键值对被添加到dict中了

3) threadA’s user delegate completes, and the thread arrives at the lock, but now sees that the item exists already

线A的委托执行完成了,并且也获得了GetOrAdd的锁,但是现在发现相关数据已经存在了

4) threadA performs a "Get", and returns the data th at was previously added by threadB.

线程A执行“Get”,返回之前被线程B添加的数据

不止这个英文原帖,好多国内的帖子都在说这个问题。

然而我觉得这并不是个问题,这最多是一个编程时需要注意的事项,微软的这个设计是合理的:ConcurrentDictionary没有理由也没有职责去把外部代码加锁,外部代码的同步应该由

外部控制。回头看看上面的解释,我们发现只不过是传递给ConcurrentDictionary的委托被执行了不止一次,但数据仍然只添加了一次,ConcurrentDictionary已经完全履行了自己的职责,而且MSDN也作了相关的说明(事实上,上面的四句英文也来自MSDN):

The user delegate that is passed to these methods is invoked outside of the dictionary‘s internal lock. (This is done to prevent unknown code from blocking all threads.)

传递进来的用户委托在字典内部锁的外面调用,这么做是为了防止阻塞所有相关线程,具体参考MSDN

时间: 2024-08-01 18:12:45

都说ConcurrentDictionary<TKey, TValue>有陷阱的相关文章

.net源码分析 - ConcurrentDictionary&lt;TKey, TValue&gt;

List源码分析 Dictionary源码分析 ConcurrentDictionary源码分析 分析: http://www.cnblogs.com/brookshi/p/5583892.html 继上篇Dictionary源码分析,上篇讲过的在这里不会再重复 ConcurrentDictionary源码地址:https://github.com/dotnet/corefx/blob/master/src/System.Collections.Concurrent/src/System/Col

c# 扩展方法奇思妙用基础篇五:Dictionary&lt;TKey, TValue&gt; 扩展

Dictionary<TKey, TValue>类是常用的一个基础类,但用起来有时确不是很方便.本文逐一讨论,并使用扩展方法解决. 向字典中添加键和值 添加键和值使用 Add 方法,但很多时候,我们是不敢轻易添加的,因为 Dictionary<TKey, TValue>不允许重复,尝试添加重复的键时 Add 方法引发 ArgumentException. 大多时候,我们都会写成以下的样子: var dict = new Dictionary<int, string>()

C#高级编程五十三天----字典Dictionary&lt;TKey,TValue&gt;

字典 关键字:Dicitionary 说明: 必须包含命名空间System.Collection.Generic Dictionary里面的每一个元素都是一个键值对(由两个元组组成:键和值). 键必须是唯一的,而值不需要唯一的. 键和值都可以是任意类型(例如:string,int,自定义类型,等等) 通过一个键读取一个值的事件是接近O(1) 键值对之间的偏序可以不定义 使用案例: using System; using System.Collections.Generic; using Syst

.net源码分析 – Dictionary&lt;TKey, TValue&gt;

接上篇:.net源码分析 – List<T> Dictionary<TKey, TValue>源码地址:https://github.com/dotnet/corefx/blob/master/src/System.Collections/src/System/Collections/Generic/Dictionary.cs 接口 Dictionary<TKey, TValue>和List<T>的接口形式差不多,不重复说了,可以参考List<T>

C#中数组、集合(ArrayList)、泛型集合List&lt;T&gt;、字典(dictionary&lt;TKey,TValue&gt;)全面对比

为什么把这4个东西放在一起来说,因为c#中的这4个对象都是用来存储数据的集合--. 首先咱们把这4个对象都声明并实例化一下: //数组 string[] m_Str = new string[5]; //集合 ArrayList m_AList = new ArrayList(); //泛型集合 List<int> m_List = new List<int>(); //字典 Dictionary<int, string> m_Dt = new Dictionary&l

Dictionary&lt;TKey, TValue&gt; 类

C# Dictionary<TKey, TValue> 类 Dictionary<TKey, TValue> 泛型类提供了从一组键到一组值的映射.字典中的每个添加项都由一个值及其相关联的键组成.通过key检索值的速度非常快,其时间复杂度为常数阶 O(1),因为 Dictionary<TKey, TValue> 类是以哈希表的方式实现的. 只要对象用作键在 Dictionary<TKey, TValue>,不得更改任何会影响其哈希值的方式.每个在 Dictio

.NET中Dictionary&lt;TKey, TValue&gt;浅析

.NET中Dictionary<TKey, Tvalue>是非常常用的key-value的数据结构,也就是其实就是传说中的哈希表..NET中还有一个叫做Hashtable的类型,两个类型都是哈希表.这两个类型都可以实现键值对存储的功能,区别就是一个是泛型一个不是并且内部实现有一些不同.今天就研究一下.NET中的Dictionary<TKey, TValue>以及一些相关问题. guid:33b4b911-2068-4513-9d98-31b2dab4f70c 文中如有错误,望指出.

C#编程(五十三)----------字典Dictionary&lt;TKey,TValue&gt;

字典 关键字:Dicitionary 说明: 必须包含命名空间System.Collection.Generic Dictionary里面的每一个元素都是一个键值对(由两个元组组成:键和值). 键必须是唯一的,而值不需要唯一的. 键和值都可以是任意类型(例如:string,int,自定义类型,等等) 通过一个键读取一个值的事件是接近O(1) 键值对之间的偏序可以不定义 使用案例: using System; using System.Collections.Generic; using Syst

IDictionary&lt;TKey,TValue&gt;数据字典使用讲解

接口描述 Represents a nongeneric collection of key/value pairs.[代表一个非泛型的键/值对的集合].在System.Collections.Generic包下面.所在程序集为mscorlib.dll中. 语法 public Interface IDictionary<TKey,TValue> :ICollection<KeyValuePair<TKey,TValue>>,IEnumerable<KeyValue