• 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
  • 相关阅读:
    业务需求、用户需求和功能需求
    乐观锁的两种实现方式
    数据字典
    freemarker(ftl)标签用法
    commons-lang常用方法
    前端与后端分离
    jar包导入本地maven库的操作
    本地打jar包到本地的Maven出库
    MyEclipse中好用的快捷键汇总整理
    简单的反编译class文件并重新编译的方法
  • 原文地址:https://www.cnblogs.com/xuejietong/p/11984847.html
Copyright © 2020-2023  润新知