Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConcurrentDictionary Object - Reading and writing via different threads

I want to use a ConcurrentDictionary in my app, but first I need to make sure I understand correctly how it works. In my app, I'll have one or more threads that write to, or delete from, the dictionary. And, I'll have one or more threads that read from the dictionary. Potentially, all at the same time.

Am I correct that the implementation of ConcurrentDictionary takes care of all the required locking for this to happen, and I don't need to provide my own locking? In other words, if one thread is writing to, or deleting from, the dictionary, a reading thread (or another write thread) will be blocked until the update or delete is finished?

Thanks very much.

like image 493
Randy Minder Avatar asked Aug 15 '12 12:08

Randy Minder


People also ask

Is ConcurrentDictionary AddOrUpdate thread-safe?

Also, although all methods of ConcurrentDictionary<TKey,TValue> are thread-safe, not all methods are atomic, specifically GetOrAdd and AddOrUpdate. To prevent unknown code from blocking all threads, the user delegate that's passed to these methods is invoked outside of the dictionary's internal lock.

Is ConcurrentDictionary slower?

ConcurrentDictionary - "Good read speed even in the face of concurrency, but it's a heavyweight object to create and slower to update."

How does ConcurrentDictionary work 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.


1 Answers

The current implementation uses a mixture of striped locks (the technique I suggested in an answer to someone yesterday at https://stackoverflow.com/a/11950835/400547) and thinking very very hard about the situations in which an operation cannot possibly cause problems for or have problems cause by, a concurrent operation (there's quite a lot of these, but you have to be very sure if you make use of them).

As such if you have several operations happening on the concurrent dictionary at once, each of the following is possible:

  1. No threads even lock, but everything happens correctly.
  2. Some threads lock, but they lock on separate things, and there is no lock contention.
  3. One or two threads have lock contention with each other, and are slowed down, but the effect upon performance is less than if there were a single lock.
  4. One or two threads need to lock the entire thing for a while (generally for internal resizing) which blocks all the threads that could possibly be blocked in case 3 above, though some can keep going (those that read).

None of this involves dirty reads, which is a matter only vaguely related to locking (my own form of concurrent dictionary uses no locks at all, and it doesn't have dirty reads either).

This thread-safety doesn't apply to batches done by your code (if you read a value and then write a value, the value read may have changed before you finished the write), but note that some common cases which would require a couple of calls on Dictionary are catered for by single methods on ConcurrentDictionary (GetOrAdd and AddOrUpdate do things that would be two calls with a Dictionary so they can be done atomically - though note that the Func involved in some overloads may be called more than once).

Due to this, there's no added danger with ConcurrentDictionary, so you should pick as follows:

If you're going to have to lock over some batches of operations that don't match what ConcurrentDictionary offers like e.g.:

lock(lockObj)
{
  var test = dict[key1];
  var test2 = dict[key2];
  if(test < test2 && test2 < dict[key3] && SomeOtherBooleanProducer())
    dict[key4] = SomeFactoryCall(key4);
}

Then you would have to lock on ConcurrentDictionary, and while there may be a way to combine that with what it offers in the way of support for concurrency, there probably won't, so just use Dictionary with a lock.

Otherwise it comes down to how much concurrent hits there will probably be. If you're mostly only going to have one thread hitting the dictionary, but you need to guard against the possibility of concurrent access, then you should definitely go for Dictionary with a lock. If you're going to have periods where half a dozen or more threads are hitting the dictionary, then you should definitely go for ConcurrentDictionary (if they're likely to be hitting the same small number of keys then take a look at my version because that's the one situation where I have better performance).

Just where the middle point between "few" and "many" threads lies, is hard to say. I'd say that if there are more than two threads on a regular basis then go with ConcurrentDictionary. If nothing else, demands from concurrency tend to increase throughout the lifetime of a project more often than they decrease.

Edit: To answer about the particular case you give, of one writer and one reader, there won't be any blocking at all, as that is safe for roughly the same reason why multiple readers and one writer is safe on Hashtable, though ConcurrentDictionary goes beyond that in several ways.

like image 166
Jon Hanna Avatar answered Oct 05 '22 06:10

Jon Hanna