ConcurrentDictionary
Here we handle multiple threads. This type from System.Collections.Concurrent
allows multiple threads to access a Dictionary
instance.
With this type, you get a thread-safe, hash-based lookup table. The ConcurrentDictionary
type resides in System.Collections.Concurrent
.
These 2 programs compare ConcurrentDictionary
and Dictionary
when adding keys and values. You can see they are about the same length in code.
ConcurrentDictionary
, we use the TryAdd
method. This does nothing if the key is already found.Dictionary
program, we use the Add method. The Dictionary
program will fail—you cannot Add an existing element.using System; using System.Collections.Concurrent; using System.Linq; using System.Threading; class Program { static ConcurrentDictionary<string, int> _concurrent = new ConcurrentDictionary<string, int>(); static void Main() { Thread thread1 = new Thread(new ThreadStart(A)); Thread thread2 = new Thread(new ThreadStart(A)); thread1.Start(); thread2.Start(); thread1.Join(); thread2.Join(); Console.WriteLine("Average: {0}", _concurrent.Values.Average()); } static void A() { for (int i = 0; i < 1000; i++) { _concurrent.TryAdd(i.ToString(), i); } } }using System; using System.Collections.Generic; using System.Linq; using System.Threading; class Program { static Dictionary<string, int> _dictionary = new Dictionary<string, int>(); static void Main() { Thread thread1 = new Thread(new ThreadStart(A)); Thread thread2 = new Thread(new ThreadStart(A)); thread1.Start(); thread2.Start(); thread1.Join(); thread2.Join(); Console.WriteLine("Average: {0}", _dictionary.Values.Average()); } static void A() { for (int i = 0; i < 1000; i++) { _dictionary.Add(i.ToString(), i); } } }
TryUpdate
Here we test TryUpdate
. The first call to TryUpdate
doesn't do anything, because the value of "cat" is not equal to 4.
using System; using System.Collections.Concurrent; class Program { static void Main() { // New instance. var con = new ConcurrentDictionary<string, int>(); con.TryAdd("cat", 1); con.TryAdd("dog", 2); // Try to update if value is 4 (this fails). con.TryUpdate("cat", 200, 4); // Try to update if value is 1 (this works). con.TryUpdate("cat", 100, 1); // Write new value. Console.WriteLine(con["cat"]); } }100
AddOrUpdate
GetOrAdd
These methods resolve problems with the nature of time in concurrent systems. They ensure the collection will not become corrupted or invalid.
AddOrUpdate
method will always result in a value change in the collection.GetOrAdd
method is the same as AddOrUpdate
except it will not change the existing value: it will only return it.using System; using System.Collections.Concurrent; class Program { static void Main() { // New instance. var con = new ConcurrentDictionary<string, int>(); con.TryAdd("cat", 1); con.TryAdd("dog", 2); // Add dog with value of 5 if it does NOT exist. // ... Otherwise, add one to its value. con.AddOrUpdate("dog", 5, (k, v) => v + 1); // Display dog value. Console.WriteLine(con["dog"]); // Get mouse or add it with value of 4. int mouse = con.GetOrAdd("mouse", 4); Console.WriteLine(mouse); // Get mouse or add it with value of 660. mouse = con.GetOrAdd("mouse", 660); Console.WriteLine(mouse); } }3 4 4
How fast is ConcurrentDictionary
? I tested its lookup performance. In the simple benchmark, 2000 string
keys are assigned to ints. Then the key "100" is looked up and timed.
ConcurrentDictionary
was almost twice as slow as the regular Dictionary
. The overhead in ConcurrentDictionary
was small.ConcurrentDictionary<string, int> c = new ConcurrentDictionary<string, int>(); for (int i = 0; i < 2000; i++) { c[i.ToString()] = i; }Dictionary<string, int> d = new Dictionary<string, int>(); for (int i = 0; i < 2000; i++) { d[i.ToString()] = i; }int v; if (c.TryGetValue("100", out v)) { if (v != 100) { throw new Exception(); } }int v; if (d.TryGetValue("100", out v)) { if (v != 100) { throw new Exception(); } }74.50 ns ConcurrentDictionary 43.40 ns Dictionary
ToArray
The ToArray
method on the ConcurrentDictionary
type yields an array of KeyValuePair
structs. We can use this like any other array.
IsEmpty
This property is equivalent to the expression Count
== 0. The collection can never contain a negative number of elements, and is only empty when there are zero elements.
We looked at the ConcurrentDictionary
type and its intended usage. The ConcurrentDictionary
can enhance lookup performance in complex, time-critical software.