Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is ConcurrentDictionary Keys or Values property threadsafe

Have a question regarding thread safety with ConcurrentDictionary. From the API, I see that the enumerator is thread-safe, but I don't see the same for keys and values properties. My question is:

Is it safe to loop over the Keys or Values collection when there are other threads modifying it concurrently?

like image 777
Koda Avatar asked May 07 '12 09:05

Koda


People also ask

Is ConcurrentDictionary values thread-safe?

All public and protected members of ConcurrentDictionary are thread-safe and may be used concurrently from multiple threads. Since the . Values property is an implementation dictated by the IColletion interface it is public and therefore thread safe.

What is the use of ConcurrentDictionary in C#?

ConcurrentDictionary is thread-safe collection class to store key/value pairs. It internally uses locking to provide you a thread-safe class. It provides different methods as compared to Dictionary class. We can use TryAdd, TryUpdate, TryRemove, and TryGetValue to do CRUD operations on ConcurrentDictionary.

What is the purpose of the ConcurrentDictionary TKey TValue class?

Represents a thread-safe collection of key/value pairs that can be accessed by multiple threads concurrently.

Is ConcurrentDictionary clear thread-safe?

Concurrent. ConcurrentDictionary<TKey,TValue>. This collection class is a thread-safe implementation.


1 Answers

While I do like the documentation, I tend to verify things with a small program when in doubt or I feel that I might be assuming too much.

The following code verifies that indeed you can enumerate the values collection safely while adding or removing keys from a separate thread to that on which the enumeration is taking place. This will not cause the usual collection was modified exceptions. In more detail, here are a couple of test cases

Case 1: Enumerating Values and deleting a key

If you follow the following sequence:

  • start enumerating the values collection from a thread
  • remove a key from a different thread that we have not enumerated yet
  • Continue enumerating on the original thread

The observed behavior is that the removed key will indeed be enumerated since it existed in the values collection when we started the enumeration. No exception will be raised.

Case 2: Enumerating Values and adding a key

  • start enumerating the values collection from a thread
  • add a new key from a different thread that we have not enumerated yet
  • Continue enumerating on the original thread

The observed behavior is that the added key will not be enumerated since it did not exist in values collection when we started to enumerate it. No exception will be raised whether we use TryAdd or add by assigning directly to the dictionary ie dictionary[key] = value.

Sample Code

Here is the sample program that demonstrates both cases:

ConcurrentDictionary<int, int> dictionary = new ConcurrentDictionary<int, int>();  // Seed the dictionary with some arbitrary values;  for (int i = 0; i < 30; i++) {     dictionary.TryAdd(i, i); }  // Reader thread - Enumerate the Values collection Task.Factory.StartNew(         () =>         {             foreach (var item in dictionary.Values)             {                 Console.WriteLine("Item {0}: count: {1}", item, dictionary.Count);                 Thread.Sleep(20);             }          } );  // writer thread - Modify dictionary by adding new items and removing existing ones from the end Task.Factory.StartNew(         () =>         {             for (int i = 29; i >= 0; i--)             {                 Thread.Sleep(10);                 //Remove an existing entry                  int removedValue;                 if (dictionary.TryRemove(i, out removedValue))                     Console.WriteLine("Removed item {0}", removedValue);                 else                     Console.WriteLine("Did not remove item {0}", i);                  int iVal = 50 + i*2;                 dictionary[iVal] = iVal;                 Thread.Sleep(10);                 iVal++;                 dictionary.TryAdd(iVal, iVal);             }         } );  Console.ReadKey(); 

And here is the output in release mode:

Console output

like image 53
Anastasiosyal Avatar answered Sep 18 '22 15:09

Anastasiosyal