Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do ConcurrentQueue and ConcurrentDictionary have "Try" methods - TryAdd, TryDequeue - instead of Add and Dequeue?

ConcurrentQueue has TryDequeue method.

Queue has just Dequeue method.

In ConcurrentDictionary there is no Add method, but we have TryAdd instead.

My question is:

What is the diffrence between these concurrent collection methods? Why they are diffrent for concurrent collections?

like image 670
Kamil Avatar asked Dec 05 '22 17:12

Kamil


1 Answers

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.

like image 66
Scott Hannen Avatar answered Jan 02 '23 03:01

Scott Hannen