Home
Map
ConcurrentDictionary ExampleUse ConcurrentDictionary and its methods such as TryAdd and GetOrUpdate.
C#
This page was last reviewed on May 18, 2023.
ConcurrentDictionary. Here we handle multiple threads. This type from System.Collections.Concurrent allows multiple threads to access a Dictionary instance.
Type notes. With this type, you get a thread-safe, hash-based lookup table. The ConcurrentDictionary type resides in System.Collections.Concurrent.
ConcurrentBag
Example. These 2 programs compare ConcurrentDictionary and Dictionary when adding keys and values. You can see they are about the same length in code.
Dictionary
Tip With ConcurrentDictionary, we use the TryAdd method. This does nothing if the key is already found.
And In the 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.
Info The second call, though, does change the value from 1 to 100 because its value was set to 1.
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.
Detail Conceptually, the AddOrUpdate method will always result in a value change in the collection.
Detail The 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
Performance. 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.
Benchmark
Result The ConcurrentDictionary was almost twice as slow as the regular Dictionary. The overhead in ConcurrentDictionary was small.
Note I was expecting it to perform worse. Its performance would not interfere in many programs.
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
Try methods. Try methods attempt to perform an action upon the contents of the collection, but return false if they cannot. The given key may be invalid.
Detail This method is the same as the Add method on Dictionary except it doesn't throw an exception if the key is already present.
Detail This is the same as the TryGetValue method on the Dictionary type from System.Collections.Generic.
Detail This is different from the Dictionary Remove method. It has a second, out parameter that returns the value that was removed.
Detail This can be used to update a key with the value you provide to a new value.
ToArray. The ToArray method on the ConcurrentDictionary type yields an array of KeyValuePair structs. We can use this like any other array.
KeyValuePair
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.
C#VB.NETPythonGoJavaSwiftRust
Dot Net Perls is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on May 18, 2023 (edit).
Home
Changes
© 2007-2023 Sam Allen.