ConcurrentDictionary源码概读

Posted xuejietong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ConcurrentDictionary源码概读相关的知识,希望对你有一定的参考价值。

  ConcurrentDictionary的数据结构主要由Tables和Node组成,其中Tables包括桶(Node,节点)数组、局部锁(Local lock)、每个锁保护的元素数量(PerLock)。Node包含用户实际操作的key和value,以及为实现链表数据结构的下一个节点(Next Node)的引用和当前节点key的原始(未取正)散列值。以及其它一些标识。

 1         private class Tables
 2         {
 3             /// <summary>
 4             /// 每个桶的单链表
 5             /// </summary>
 6             internal readonly Node[] m_buckets; 
 7 
 8             /// <summary>
 9             /// 锁数组,每个锁都锁住table的一部分
10             /// </summary>
11             internal readonly object[] m_locks;
12 
13             /// <summary>
14             /// 每个锁保护的元素的数量
15             /// </summary>
16             internal volatile int[] m_countPerLock; 
17 
18             /// <summary>
19             /// key的比较器
20             /// </summary>
21             internal readonly IEqualityComparer<TKey> m_comparer; 
22 
23             internal Tables(Node[] buckets, object[] locks, int[] countPerLock, IEqualityComparer<TKey> comparer)
24             {
25                 m_buckets = buckets;
26                 m_locks = locks;
27                 m_countPerLock = countPerLock;
28                 m_comparer = comparer;
29             }
30         }
31 
32         private class Node
33         {
34             internal TKey m_key;
35             internal TValue m_value;
36             internal volatile Node m_next;
37             internal int m_hashcode;
38 
39             internal Node(TKey key, TValue value, int hashcode, Node next)
40             {
41                 m_key = key;
42                 m_value = value;
43                 m_next = next;
44                 m_hashcode = hashcode;
45             }
46         }

  当new一个ConcurrentDictionary时,默认调用无参构造函数,给定默认的并发数量(Environment.ProcessorCount)、默认的键比较器、默认的容量(桶的初始容量,为31),该容量是经过权衡得到,不能被最小的素数整除。之后再处理容量与并发数的关系、容量与锁的关系以及每个锁的最大元素数。将桶、锁对象、锁保护封装在一个对象中,并初始化。

 1         //初始化 ConcurrentDictionary 类的新实例,
 2         //该实例为空,具有默认的并发级别和默认的初始容量,并为键类型使用默认比较器。
 3         public ConcurrentDictionary() : 
 4             this(DefaultConcurrencyLevel, DEFAULT_CAPACITY, true, EqualityComparer<TKey>.Default) { }
 5 
 6         /// <summary>
 7         /// 无参构造函数真正调用的函数
 8         /// </summary>
 9         /// <param name="concurrencyLevel">并发线程的可能数量(更改字典的线程可能数量)</param>
10         /// <param name="capacity">容量</param>
11         /// <param name="growLockArray">是否动态增加 striped lock 的大小</param>
12         /// <param name="comparer">比较器</param>
13         internal ConcurrentDictionary(int concurrencyLevel, int capacity, bool growLockArray, IEqualityComparer<TKey> comparer)
14         {
15             if (concurrencyLevel < 1)
16             {
17                 throw new ArgumentOutOfRangeException("concurrencyLevel", GetResource("ConcurrentDictionary_ConcurrencyLevelMustBePositive"));
18             }
19             if (capacity < 0)
20             {
21                 throw new ArgumentOutOfRangeException("capacity", GetResource("ConcurrentDictionary_CapacityMustNotBeNegative"));
22             }
23             if (comparer == null) throw new ArgumentNullException("comparer");
24 
25             //容量应当至少与并发数一致,否则会有锁对象浪费
26             if (capacity < concurrencyLevel)
27             {
28                 capacity = concurrencyLevel;
29             }
30 
31             //锁对象数组,大小为 并发线程的可能数量
32             object[] locks = new object[concurrencyLevel];
33             for (int i = 0; i < locks.Length; i++)
34             {
35                 locks[i] = new object();
36             }
37 
38             //每个锁保护的元素的数量
39             int[] countPerLock = new int[locks.Length];
40             //单链表中的节点,表示特定的哈希存储桶(桶:Node类型的数组)。
41             Node[] buckets = new Node[capacity];
42             //可以保持字典内部状态的表,将桶、锁对象、锁保护封装在一个对象中,以便一次原子操作
43             m_tables = new Tables(buckets, locks, countPerLock, comparer);
44             //是否动态增加 striped lock 的大小
45             m_growLockArray = growLockArray;
46             //在调整大小操作被触发之前,每个锁可锁住的最大(预计)元素数
47             //默认按锁个数平均分配,即Node总个数除以锁总个数
48             m_budget = buckets.Length / locks.Length;
49         }

  当调用TryAdd时,实际调用的是内部公共方法TryAddInternal。如果存在key,则始终返回false,如果updateIfExists为true,则更新value,如果不存在key,则始终返回true,并且添加value。详细解读见代码。

技术图片
  1         /// <summary>
  2         /// 尝试将指定的键和值添加到字典
  3         /// </summary>
  4         /// <param name="key">要添加的元素的键</param>
  5         /// <param name="value">要添加的元素的值。对于引用类型,该值可以是空引用</param>
  6         /// <returns>键值对添加成功则返回true,否则false</returns>
  7         /// 异常:
  8         //   T:System.ArgumentNullException:
  9         //     key 为 null。
 10         //   T:System.OverflowException:
 11         //     字典中已包含元素的最大数量(System.Int32.MaxValue)。
 12         public bool TryAdd(TKey key, TValue value)
 13         {
 14             if (key == null) throw new ArgumentNullException("key");
 15             TValue dummy;
 16             return TryAddInternal(key, value, false, true, out dummy);
 17         }
 18 
 19         /// <summary>
 20         /// 对字典添加和修改的内部公共方法
 21         /// 如果存在key,则始终返回false,如果updateIfExists为true,则更新value
 22         /// 如果不存在key,则始终返回true,并且添加value
 23         /// </summary>
 24         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
 25         private bool TryAddInternal(TKey key, TValue value, bool updateIfExists, bool acquireLock, out TValue resultingValue)
 26         {
 27             while (true)
 28             {
 29                 //桶序号(下标),锁序号(下标)
 30                 int bucketNo, lockNo;
 31                 int hashcode;
 32 
 33                 Tables tables = m_tables;
 34                 IEqualityComparer<TKey> comparer = tables.m_comparer;
 35                 hashcode = comparer.GetHashCode(key);
 36 
 37                 //获取桶下标、锁下标
 38                 GetBucketAndLockNo(hashcode, out bucketNo, out lockNo, tables.m_buckets.Length, tables.m_locks.Length);
 39 
 40                 bool resizeDesired = false;
 41                 bool lockTaken = false;
 42 #if FEATURE_RANDOMIZED_STRING_HASHING
 43 #if !FEATURE_CORECLR                
 44                 bool resizeDueToCollisions = false;
 45 #endif // !FEATURE_CORECLR
 46 #endif
 47 
 48                 try
 49                 {
 50                     if (acquireLock)
 51                         //根据上面得到的锁的下标(lockNo),获取对应(lockNo)的对象锁
 52                         //hash落在不同的锁对象上,因此不同线程获取锁的对象可能不同,降低了“抢锁”概率
 53                         Monitor.Enter(tables.m_locks[lockNo], ref lockTaken);
 54 
 55                     //在这之前如果tables被修改则有可能未正确锁定,此时需要重试
 56                     if (tables != m_tables)
 57                     {
 58                         continue;
 59                     }
 60 
 61 #if FEATURE_RANDOMIZED_STRING_HASHING
 62 #if !FEATURE_CORECLR
 63                     int collisionCount = 0;
 64 #endif // !FEATURE_CORECLR
 65 #endif
 66 
 67                     // Try to find this key in the bucket
 68                     Node prev = null;
 69                     for (Node node = tables.m_buckets[bucketNo]; node != null; node = node.m_next)
 70                     {
 71                         Assert((prev == null && node == tables.m_buckets[bucketNo]) || prev.m_next == node);
 72                         //如果key已经存在
 73                         if (comparer.Equals(node.m_key, key))
 74                         {
 75                             //如果允许更新,则更新该键值对的值
 76                             if (updateIfExists)
 77                             {
 78                                 //如果可以原子操作则直接赋值
 79                                 if (s_isValueWriteAtomic)
 80                                 {
 81                                     node.m_value = value;
 82                                 }
 83                                 //否则需要为更新创建一个新的节点,以便支持不能以原子方式写的类型,
 84                                 //因为无锁读取也可能在此时发生
 85                                 else
 86                                 {
 87                                     //node.m_next 新节点指向下一个节点
 88                                     Node newNode = new Node(node.m_key, value, hashcode, node.m_next);
 89                                     if (prev == null)
 90                                     {
 91                                         tables.m_buckets[bucketNo] = newNode;
 92                                     }
 93                                     else
 94                                     {
 95                                         //上一个节点指向新节点。此时完成单链表的新旧节点替换
 96                                         prev.m_next = newNode;
 97                                     }
 98                                 }
 99                                 resultingValue = value;
100                             }
101                             else
102                             {
103                                 resultingValue = node.m_value;
104                             }
105                             return false;
106                         }
107                         //循环到最后时,prev是最后一个node(node.m_next==null)
108                         prev = node;
109 
110 #if FEATURE_RANDOMIZED_STRING_HASHING
111 #if !FEATURE_CORECLR
112                         collisionCount++;
113 #endif // !FEATURE_CORECLR
114 #endif
115                     }
116 
117 #if FEATURE_RANDOMIZED_STRING_HASHING
118 #if !FEATURE_CORECLR
119                     if(collisionCount > HashHelpers.HashCollisionThreshold && HashHelpers.IsWellKnownEqualityComparer(comparer)) 
120                     {
121                         resizeDesired = true;
122                         resizeDueToCollisions = true;
123                     }
124 #endif // !FEATURE_CORECLR
125 #endif
126 
127                     //使用可变内存操作插入键值对
128                     Volatile.Write<Node>(ref tables.m_buckets[bucketNo], new Node(key, value, hashcode, tables.m_buckets[bucketNo]));
129                     checked
130                     {
131                         //第lockNo个锁保护的元素数量,并检查是否益处
132                         tables.m_countPerLock[lockNo]++;
133                     }
134 
135                     //
136                     // If the number of elements guarded by this lock has exceeded the budget, resize the bucket table.
137                     // It is also possible that GrowTable will increase the budget but won‘t resize the bucket table.
138                     // That happens if the bucket table is found to be poorly utilized due to a bad hash function.
139                     //如果第lockNo个锁要锁的元素超出预计,则需要调整
140                     if (tables.m_countPerLock[lockNo] > m_budget)
141                     {
142                         resizeDesired = true;
143                     }
144                 }
145                 finally
146                 {
147                     if (lockTaken)
148                         //释放第lockNo个锁
149                         Monitor.Exit(tables.m_locks[lockNo]);
150                 }
151 
152                 //
153                 // The fact that we got here means that we just performed an insertion. If necessary, we will grow the table.
154                 //
155                 // Concurrency notes:
156                 // - Notice that we are not holding any locks at when calling GrowTable. This is necessary to prevent deadlocks.
157                 // - As a result, it is possible that GrowTable will be called unnecessarily. But, GrowTable will obtain lock 0
158                 //   and then verify that the table we passed to it as the argument is still the current table.
159                 //
160                 if (resizeDesired)
161                 {
162 #if FEATURE_RANDOMIZED_STRING_HASHING
163 #if !FEATURE_CORECLR
164                     if (resizeDueToCollisions)
165                     {
166                         GrowTable(tables, (IEqualityComparer<TKey>)HashHelpers.GetRandomizedEqualityComparer(comparer), true, m_keyRehashCount);
167                     }
168                     else
169 #endif // !FEATURE_CORECLR
170                     {
171                         GrowTable(tables, tables.m_comparer, false, m_keyRehashCount);
172                     }
173 #else
174                     GrowTable(tables, tables.m_comparer, false, m_keyRehashCount);
175 #endif
176                 }
177 
178                 resultingValue = value;
179                 return true;
180             }
181         }
View Code

  需要特别指出的是ConcurrentDictionary在插入、更新、获取键值对时对key的比较默认是使用的引用比较,不同于Dictionary使用引用加散列值。在Dictionary中,只有两者都一致才相等,ConcurrentDictionary则只判断引用相等。前提是未重写Equals。

 1         /// <summary>
 2         /// Attempts to get the value associated with the specified key from the <see
 3         /// cref="ConcurrentDictionary{TKey,TValue}"/>.
 4         /// </summary>
 5         /// <param name="key">The key of the value to get.</param>
 6         /// <param name="value">When this method returns, <paramref name="value"/> contains the object from
 7         /// the
 8         /// <see cref="ConcurrentDictionary{TKey,TValue}"/> with the specified key or the default value of
 9         /// <typeparamref name="TValue"/>, if the operation failed.</param>
10         /// <returns>true if the key was found in the <see cref="ConcurrentDictionary{TKey,TValue}"/>;
11         /// otherwise, false.</returns>
12         /// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
13         /// (Nothing in Visual Basic).</exception>
14         [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
15         public bool TryGetValue(TKey key, out TValue value)
16         {
17             if (key == null) throw new ArgumentNullException("key");
18 
19             int bucketNo, lockNoUnused;
20 
21             // We must capture the m_buckets field in a local variable. It is set to a new table on each table resize.
22             Tables tables = m_tables;
23             IEqualityComparer<TKey> comparer = tables.m_comparer;
24             GetBucketAndLockNo(comparer.GetHashCode(key), out bucketNo, out lockNoUnused, tables.m_buckets.Length, tables.m_locks.Length);
25 
26             // We can get away w/out a lock here.
27             // The Volatile.Read ensures that the load of the fields of ‘n‘ doesn‘t move before the load from buckets[i].
28             Node n = Volatile.Read<Node>(ref tables.m_buckets[bucketNo]);
29 
30             while (n != null)
31             {
32                 //默认比较的是引用
33                 if (comparer.Equals(n.m_key, key))
34                 {
35                     value = n.m_value;
36                     return true;
37                 }
38                 n = n.m_next;
39             }
40 
41             value = default(TValue);
42             return false;
43         }

  其它一些需要知道的内容,比如默认并发数、如何为指定key计算桶号和锁号等

技术图片
  1 #if !FEATURE_CORECLR
  2         [NonSerialized]
  3 #endif
  4         private volatile Tables m_tables; // Internal tables of the dictionary       
  5         // NOTE: this is only used for compat reasons to serialize the comparer.
  6         // This should not be accessed from anywhere else outside of the serialization methods.
  7         internal IEqualityComparer<TKey> m_comparer;
  8 #if !FEATURE_CORECLR
  9         [NonSerialized]
 10 #endif
 11         private readonly bool m_growLockArray; // Whether to dynamically increase the size of the striped lock
 12 
 13         // How many times we resized becaused of collisions. 
 14         // This is used to make sure we don‘t resize the dictionary because of multi-threaded Add() calls
 15         // that generate collisions. Whenever a GrowTable() should be the only place that changes this
 16 #if !FEATURE_CORECLR
 17         // The field should be have been marked as NonSerialized but because we shipped it without that attribute in 4.5.1.
 18         // we can‘t add it back without breaking compat. To maximize compat we are going to keep the OptionalField attribute 
 19         // This will prevent cases where the field was not serialized.
 20         [OptionalField]
 21 #endif
 22         private int m_keyRehashCount;
 23 
 24 #if !FEATURE_CORECLR
 25         [NonSerialized]
 26 #endif
 27         private int m_budget; // The maximum number of elements per lock before a resize operation is triggered
 28 
 29 #if !FEATURE_CORECLR // These fields are not used in CoreCLR
 30         private KeyValuePair<TKey, TValue>[] m_serializationArray; // Used for custom serialization
 31 
 32         private int m_serializationConcurrencyLevel; // used to save the concurrency level in serialization
 33 
 34         private int m_serializationCapacity; // used to save the capacity in serialization
 35 #endif
 36 
 37 
 38         // The default capacity, i.e. the initial # of buckets. When choosing this value, we are making
 39         // a trade-off between the size of a very small dictionary, and the number of resizes when
 40         // constructing a large dictionary. Also, the capacity should not be divisible by a small prime.
 41         private const int DEFAULT_CAPACITY = 31;
 42 
 43         // The maximum size of the striped lock that will not be exceeded when locks are automatically
 44         // added as the dictionary grows. However, the user is allowed to exceed this limit by passing
 45         // a concurrency level larger than MAX_LOCK_NUMBER into the constructor.
 46         private const int MAX_LOCK_NUMBER = 1024;
 47 
 48         private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000; // How often to refresh the count, in milliseconds.
 49         private static volatile int s_processorCount; // The last count seen.
 50         private static volatile int s_lastProcessorCountRefreshTicks; // The last time we refreshed.
 51 
 52         /// <summary>
 53         /// Gets the number of available processors
 54         /// </summary>
 55         private static int ProcessorCount
 56         {
 57             get
 58             {
 59                 int now = Environment.TickCount;
 60                 int procCount = s_processorCount;
 61                 if (procCount == 0 || (now - s_lastProcessorCountRefreshTicks) >= PROCESSOR_COUNT_REFRESH_INTERVAL_MS)
 62                 {
 63                     s_processorCount = procCount = Environment.ProcessorCount;
 64                     s_lastProcessorCountRefreshTicks = now;
 65                 }
 66 
 67                 Contract.Assert(procCount > 0 && procCount <= 64,
 68                     "Processor count not within the expected range (1 - 64).");
 69 
 70                 return procCount;
 71             }
 72         }
 73 
 74         // Whether TValue is a type that can be written atomically (i.e., with no danger of torn reads)
 75         private static readonly bool s_isValueWriteAtomic = IsValueWriteAtomic();
 76         /// <summary>
 77         /// The number of concurrent writes for which to optimize by default.
 78         /// </summary>
 79         private static int DefaultConcurrencyLevel
 80         {
 81             get { return ProcessorCount; }
 82         }
 83         /// <summary>
 84         /// Replaces the bucket table with a larger one. To prevent multiple threads from resizing the
 85         /// table as a result of ----s, the Tables instance that holds the table of buckets deemed too
 86         /// small is passed in as an argument to GrowTable(). GrowTable() obtains a lock, and then checks
 87         /// the Tables instance has been replaced in the meantime or not. 
 88         /// The <paramref name="rehashCount"/> will be used to ensure that we don‘t do two subsequent resizes
 89         /// because of a collision
 90         /// </summary>
 91         private void GrowTable(Tables tables, IEqualityComparer<TKey> newComparer, bool regenerateHashKeys, int rehashCount)
 92         {
 93             int locksAcquired = 0;
 94             try
 95             {
 96                 // The thread that first obtains m_locks[0] will be the one doing the resize operation
 97                 AcquireLocks(0, 1, ref locksAcquired);
 98 
 99                 if (regenerateHashKeys && rehashCount == m_keyRehashCount)
100                 {
101                     // This method is called with regenerateHashKeys==true when we detected 
102                     // more than HashHelpers.HashCollisionThreshold collisions when adding a new element.
103                     // In that case we are in the process of switching to another (randomized) comparer
104                     // and we have to re-hash all the keys in the table.
105                     // We are only going to do this if we did not just rehash the entire table while waiting for the lock
106                     tables = m_tables;
107                 }
108                 else
109                 {
110                     // If we don‘t require a regeneration of hash keys we want to make sure we don‘t do work when
111                     // we don‘t have to
112                     if (tables != m_tables)
113                     {
114                         // We assume that since the table reference is different, it was already resized (or the budget
115                         // was adjusted). If we ever decide to do table shrinking, or replace the table for other reasons,
116                         // we will have to revisit this logic.
117                         return;
118                     }
119 
120                     // Compute the (approx.) total size. Use an Int64 accumulation variable to avoid an overflow.
121                     long approxCount = 0;
122                     for (int i = 0; i < tables.m_countPerLock.Length; i++)
123                     {
124                         approxCount += tables.m_countPerLock[i];
125                     }
126 
127                     //
128                     // If the bucket array is too empty, double the budget instead of resizing the table
129                     //
130                     if (approxCount < tables.m_buckets.Length / 4)
131                     {
132                         m_budget = 2 * m_budget;
133                         if (m_budget < 0)
134                         {
135                             m_budget = int.MaxValue;
136                         }
137 
138                         return;
139                     }
140                 }
141                 // Compute the new table size. We find the smallest integer larger than twice the previous table size, and not divisible by
142                 // 2,3,5 or 7. We can consider a different table-sizing policy in the future.
143                 int newLength = 0;
144                 bool maximizeTableSize = false;
145                 try
146                 {
147                     checked
148                     {
149                         // Double the size of the buckets table and add one, so that we have an odd integer.
150                         newLength = tables.m_buckets.Length * 2 + 1;
151 
152                         // Now, we only need to check odd integers, and find the first that is not divisible
153                         // by 3, 5 or 7.
154                         while (newLength % 3 == 0 || newLength % 5 == 0 || newLength % 7 == 0)
155                         {
156                             newLength += 2;
157                         }
158 
159                         Assert(newLength % 2 != 0);
160 
161                         if (newLength > Array.MaxArrayLength)
162                         {
163                             maximizeTableSize = true;
164                         }
165                     }
166                 }
167                 catch (OverflowException)
168                 {
169                     maximizeTableSize = true;
170                 }
171 
172                 if (maximizeTableSize)
173                 {
174                     newLength = Array.MaxArrayLength;
175 
176                     // We want to make sure that GrowTable will not be called again, since table is at the maximum size.
177                     // To achieve that, we set the budget to int.MaxValue.
178                     //
179                     // (There is one special case that would allow GrowTable() to be called in the future: 
180                     // calling Clear() on the ConcurrentDictionary will shrink the table and lower the budget.)
181                     m_budget = int.MaxValue;
182                 }
183 
184                 // Now acquire all other locks for the table
185                 AcquireLocks(1, tables.m_locks.Length, ref locksAcquired);
186 
187                 object[] newLocks = tables.m_locks;
188 
189                 // Add more locks
190                 if (m_growLockArray && tables.m_locks.Length < MAX_LOCK_NUMBER)
191                 {
192                     newLocks = new object[tables.m_locks.Length * 2];
193                     Array.Copy(tables.m_locks, newLocks, tables.m_locks.Length);
194 
195                     for (int i = tables.m_locks.Length; i < newLocks.Length; i++)
196                     {
197                         newLocks[i] = new object();
198                     }
199                 }
200 
201                 Node[] newBuckets = new Node[newLength];
202                 int[] newCountPerLock = new int[newLocks.Length];
203 
204                 // Copy all data into a new table, creating new nodes for all elements
205                 for (int i = 0; i < tables.m_buckets.Length; i++)
206                 {
207                     Node current = tables.m_buckets[i];
208                     while (current != null)
209                     {
210                         Node next = current.m_next;
211                         int newBucketNo, newLockNo;
212                         int nodeHashCode = current.m_hashcode;
213 
214                         if (regenerateHashKeys)
215                         {
216                             // Recompute the hash from the key
217                             nodeHashCode = newComparer.GetHashCode(current.m_key);
218                         }
219 
220                         GetBucketAndLockNo(nodeHashCode, out newBucketNo, out newLockNo, newBuckets.Length, newLocks.Length);
221 
222                         newBuckets[newBucketNo] = new Node(current.m_key, current.m_value, nodeHashCode, newBuckets[newBucketNo]);
223 
224                         checked
225                         {
226                             newCountPerLock[newLockNo]++;
227                         }
228 
229                         current = next;
230                     }
231                 }
232 
233                 // If this resize regenerated the hashkeys, increment the count
234                 if (regenerateHashKeys)
235                 {
236                     // We use unchecked here because we don‘t want to throw an exception if 
237                     // an overflow happens
238                     unchecked
239                     {
240                         m_keyRehashCount++;
241                     }
242                 }
243 
244                 // Adjust the budget
245                 m_budget = Math.Max(1, newBuckets.Length / newLocks.Length);
246 
247                 // Replace tables with the new versions
248                 m_tables = new Tables(newBuckets, newLocks, newCountPerLock, newComparer);
249             }
250             finally
251             {
252                 // Release all locks that we took earlier
253                 ReleaseLocks(0, locksAcquired);
254             }
255         }
256 
257         /// <summary>
258         /// 为指定key计算桶号和锁号
259         /// </summary>
260         /// <param name="hashcode">key的hashcode</param>
261         /// <param name="bucketNo"></param>
262         /// <param name="lockNo"></param>
263         /// <param name="bucketCount">桶数量</param>
264         /// <param name="lockCount">锁数量</param>
265         private void GetBucketAndLockNo(int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount)
266         {
267             //取正hashcode,余数恒小于除数
268             bucketNo = (hashcode & 0x7fffffff) % bucketCount;
269             //若桶下标与锁个数的余数相同,则这一簇数据都使用同一个锁(局部锁)
270             lockNo = bucketNo % lockCount;
271 
272             Assert(bucketNo >= 0 && bucketNo < bucketCount);
273             Assert(lockNo >= 0 && lockNo < lockCount);
274         }
275 
276         /// <summary>
277         /// Determines whether type TValue can be written atomically
278         /// </summary>
279         private static bool IsValueWriteAtomic()
280         {
281             Type valueType = typeof(TValue);
282 
283             //
284             // Section 12.6.6 of ECMA CLI explains which types can be read and written atomically without
285             // the risk of tearing.
286             //
287             // See http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-335.pdf
288             //
289             if (valueType.IsClass)
290             {
291                 return true;
292             }
293             switch (Type.GetTypeCode(valueType))
294             {
295                 case TypeCode.Boolean:
296                 case TypeCode.Byte:
297                 case TypeCode.Char:
298                 case TypeCode.Int16:
299                 case TypeCode.Int32:
300                 case TypeCode.SByte:
301                 case TypeCode.Single:
302                 case TypeCode.UInt16:
303                 case TypeCode.UInt32:
304                     return true;
305                 case TypeCode.Int64:
306                 case TypeCode.Double:
307                 case TypeCode.UInt64:
308                     return IntPtr.Size == 8;
309                 default:
310                     return false;
311             }
312         }
View Code

 

以上是关于ConcurrentDictionary源码概读的主要内容,如果未能解决你的问题,请参考以下文章

ConcurrentDictionary

.NET 锁定还是 ConcurrentDictionary?

.NET - 字典锁定与 ConcurrentDictionary

ConcurrentDictionary和线程

ConcurrentDictionary 的列表顺序是不是得到保证?

ConcurrentDictionary与Dictionary 替换