With a Dictionary<TKey, TValue>
it's assumed that you're going to implement your own logic to make sure duplicate keys aren't entered. For example,
if(!myDictionary.ContainsKey(key)) myDictionary.Add(key, value);
But we use Concurrent collections when we have multiple threads going and it's possible that they could both be trying to modify the dictionary at the same time.
If two threads tried to execute the above code at the same time, it's possible that myDictionary.ContainsKey(key)
could return false for both threads because they're both checking at the same time and that key hasn't been added yet. Then they both try to add the key, and one fails.
Someone reading that code who doesn't know it's multithreaded could be confused. I checked to make sure that the key wasn't in the dictionary before I added it. So how am I getting an exception?
ConcurrentDictionary.TryAdd
solves that by allowing you to "try" to add the key. If it adds the value it returns true
. If it doesn't it returns false
. But what it won't do is conflict with another TryAdd
and throw an exception.
You could do all of that yourself by wrapping the Dictionary
in a class and putting lock
statements around it to make sure only one thread at a time makes changes. ConcurrentDictionary
just does that for you and does it really well. You don't have to see all the details of how it's working - you just use it knowing that multithreading has been accounted for.
Here's a detail to look for when using a class in a multithreaded application. If you go to the documentation for ConcurrentDictionary Class and scroll to the bottom you'll see this:
Thread Safety
All public and protected members of
ConcurrentDictionary are thread-safe and may be used
concurrently from multiple threads. However, members accessed through
one of the interfaces the ConcurrentDictionary
implements, including extension methods, are not guaranteed to be
thread safe and may need to be synchronized by the caller.
In other words, multiple threads can safely read and modify the collection.
Under Dictionary Class you'll see this:
Thread Safety
A Dictionary can support multiple
readers concurrently, as long as the collection is not modified. Even
so, enumerating through a collection is intrinsically not a
thread-safe procedure. In the rare case where an enumeration contends
with write accesses, the collection must be locked during the entire
enumeration. To allow the collection to be accessed by multiple
threads for reading and writing, you must implement your own
synchronization.
Multiple threads can read keys, but if multiple threads are going to write then you need to somehow lock
the dictionary to make sure only one thread at a time tries to update.
Dictionary<TKey, TValue>
exposes a Keys
collection and Values
collection so you can enumerate the keys and values, but it warns you not to try doing that if another thread is going to be modifying the dictionary. You can't enumerate something while items are being added or removed. If you need to iterate through the keys or values then you have to lock the dictionary to prevent updates during that iteration.
ConcurrentDictionary<TKey, TValue>
assumes that there will be multiple threads reading and writing, so it doesn't even expose key or value collections for you to enumerate.