经过测试发现,多线程操作都是安全的。甚至包括在遍历的时候,进行删除操作
https://github.com/ChuckTest/ConcurrentTest
class Program { private static readonly ConcurrentDictionary<int, int> Dictionary = new ConcurrentDictionary<int, int>(); private static readonly List<string> AddSuccessfully = new List<string>(); private static readonly List<string> RemoveSuccessfully = new List<string>(); private static readonly List<string> IterateString = new List<string>(); private static int _tryRemoveCount; private static readonly Random Random = new Random(); static void Main(string[] args) { try { int j = 0; for (int i = 1; i <= 12; i++) { j++; Thread thread; if (j == 1) { thread = new Thread(DoTask1); } else if(j == 2) { thread = new Thread(DoTask2); } else if (j == 3) { thread = new Thread(DoTask3); j = 0; } else { throw new Exception(); } thread.IsBackground = false; thread.Start(i); } Console.WriteLine("======"); int timeout = 0; while (AddSuccessfully.Count != 100) { timeout++; Thread.Sleep(1000); if (timeout > 5) { break; } } timeout = 0; while (_tryRemoveCount != 5) { timeout++; Thread.Sleep(1000); if (timeout > 5) { break; } } Console.WriteLine($"addSuccessfully.Count = {AddSuccessfully.Count}"); foreach (var item in AddSuccessfully) { Console.WriteLine(item); } Console.WriteLine($"removeSuccessfully.Count = {RemoveSuccessfully.Count}"); foreach (var item in RemoveSuccessfully) { Console.WriteLine(item); } Console.WriteLine($"IterateString.Count = {IterateString.Count}"); foreach (var item in IterateString) { Console.WriteLine(item); } } catch (Exception ex) { Console.WriteLine(ex); } finally { Console.ReadLine(); } } public static void DoTask1(object obj) { Console.WriteLine($"DoTask1 with {obj} Thread id = {Thread.CurrentThread.ManagedThreadId}"); for (int i = 0; i < 100; i++) { var flag = Dictionary.TryAdd(i,i); if (flag) { AddSuccessfully.Add($"DoTask1 with {obj} add {i} successfully {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}"); //Console.WriteLine(); } Thread.Sleep(1); } } public static void DoTask2(object obj) { _tryRemoveCount++; Console.WriteLine( $"DoTask2 with {obj} Thread id = {Thread.CurrentThread.ManagedThreadId}, count = {Dictionary.Count}"); var toRemove = Random.Next(0, 100); for (int i = 0; i < 100; i++) { var flag = Dictionary.TryRemove(toRemove, out _); if (flag) { RemoveSuccessfully.Add($"DoTask2 with {obj} remove {toRemove} successfully. {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}"); } Thread.Sleep(1); } } public static void DoTask3(object obj) { _tryRemoveCount++; Console.WriteLine( $"DoTask3 with {obj} Thread id = {Thread.CurrentThread.ManagedThreadId}, count = {Dictionary.Count}"); foreach (var item in Dictionary) { IterateString.Add($"DoTask3 with {obj} iterate {item.Key} {DateTime.Now:yyyy-MM-dd HH:mm:ss.fff}"); Thread.Sleep(2); } } }
[__DynamicallyInvokable] public bool TryAdd(TKey key, TValue value) { if (key == null) { throw new ArgumentNullException("key"); } TValue tvalue; return this.TryAddInternal(key, value, false, true, out tvalue); }
private bool TryAddInternal(TKey key, TValue value, bool updateIfExists, bool acquireLock, out TValue resultingValue) { ConcurrentDictionary<TKey, TValue>.Tables tables; IEqualityComparer<TKey> comparer; bool flag; bool flag3; for (;;) { tables = this.m_tables; comparer = tables.m_comparer; int hashCode = comparer.GetHashCode(key); int num; int num2; this.GetBucketAndLockNo(hashCode, out num, out num2, tables.m_buckets.Length, tables.m_locks.Length); flag = false; bool flag2 = false; flag3 = false; try { if (acquireLock) { Monitor.Enter(tables.m_locks[num2], ref flag2); } if (tables != this.m_tables) { continue; } int num3 = 0; ConcurrentDictionary<TKey, TValue>.Node node = null; for (ConcurrentDictionary<TKey, TValue>.Node node2 = tables.m_buckets[num]; node2 != null; node2 = node2.m_next) { if (comparer.Equals(node2.m_key, key)) { if (updateIfExists) { if (ConcurrentDictionary<TKey, TValue>.s_isValueWriteAtomic) { node2.m_value = value; } else { ConcurrentDictionary<TKey, TValue>.Node node3 = new ConcurrentDictionary<TKey, TValue>.Node(node2.m_key, value, hashCode, node2.m_next); if (node == null) { tables.m_buckets[num] = node3; } else { node.m_next = node3; } } resultingValue = value; } else { resultingValue = node2.m_value; } return false; } node = node2; num3++; } if (num3 > 100 && HashHelpers.IsWellKnownEqualityComparer(comparer)) { flag = true; flag3 = true; } Volatile.Write<ConcurrentDictionary<TKey, TValue>.Node>(ref tables.m_buckets[num], new ConcurrentDictionary<TKey, TValue>.Node(key, value, hashCode, tables.m_buckets[num])); checked { tables.m_countPerLock[num2]++; if (tables.m_countPerLock[num2] > this.m_budget) { flag = true; } } } finally { if (flag2) { Monitor.Exit(tables.m_locks[num2]); } } break; } if (flag) { if (flag3) { this.GrowTable(tables, (IEqualityComparer<TKey>)HashHelpers.GetRandomizedEqualityComparer(comparer), true, this.m_keyRehashCount); } else { this.GrowTable(tables, tables.m_comparer, false, this.m_keyRehashCount); } } resultingValue = value; return true; }
[__DynamicallyInvokable]
public bool TryAdd(TKey key, TValue value)
{
if (key == null)
{
throw new ArgumentNullException("key");
}
TValue tvalue;
return this.TryAddInternal(key, value, false, true, out tvalue);
}