Thread Safety of a Dictionary<Tkey, Tvalue>

Is DictionaryKey, value is thread safe for read, write and remove operation

No. A dictionary is not thread safe.

In fact, it's documentation specifically states that -

A Dictionary<TKey,TValue> 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.

For thread-safe alternatives, see the ConcurrentDictionary<TKey,TValue> class or ImmutableDictionary<TKey,TValue> class.

Is accessing the DictionaryTKey, TValue Keys property thread safe?

No, Dictionary is not thread-safe. If you need thread safety and you're using .NET 4 or higher, you should use a ConcurrentDictionary.

From MSDN:

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.

Dictionary Update Thread-Safety

No, Dictionary<TKey, TValue> class is not thread-safe for modification, as can be seen in the documentation:

A Dictionary<TKey, TValue> 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.

In your case, if some threads will finish the LongTaskToGenerateString almost simultaneously their dictionary updates will interference.

You might either use the SyncRoot property to synchronize the access manually, or just take the ConcurrentDictionary<TKey, TValue> class, as suggested by asawyer in the comments.

This implementation suggests that if you're only updating the values for existing keys, it should be OK (looking also at this) - the inelegant effect could be inaccurate value of the version property. It is used to guard against modifying the collection while it's being enumerated, so it doesn't really matter at which value it will end up. Don't know about any guarantees about this though.

c# when I use only TryGetValue on dictionary.. it's thread-safe?

Yes, it's thread safe if you only read it/ use TryGetValue:

Documentation:

A Dictionary<TKey, TValue> 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.

Are nested Dictionaries thread safe from parent ConcurrentDictionary?

Of course not, they're different dictionaries with different access rules.

Your example however is fine, because you're accessing different dictionaries from different threads. If you were to do this instead:

outerDict[30]["a"] = "b"; // in thread 1
outerDict[30]["g"] = "h"; // in thread 2

You'd quickly run into issues.

Thread safety of concurrent writes with discrete keys in Dictionary, indexer

First of all, I want to note that I got similar situation in recent project, where we have a dictionary with keys a DateTimes (unique), dealing with it in parallel, and after initializing it we sometimes got issues with KeyNotFoundException, but we didn't preallocate the memory like you. May be the issues are solved with it? Let's talk about code you've linked.

My teacher for multi-threading programming always tells us the very same thing each time we got a question regarding the concurrency:

What if billions of threads will be here right in this moment?

So let's try to see is there a possible problem in the Dictionary.

dictionary[key] = new object() leads us to

set {
Insert(key, value, false);
}

Insert is the main adding method, being called from many places in the Dictionary class. As you state the objects are unique, I assume that there will be no hash collisions there, and no overriding the values in first loop of methods, so let's see the rest of the code:

int index;
if (freeCount > 0) {
index = freeList;
freeList = entries[index].next;
freeCount--;
}
else {
if (count == entries.Length)
{
Resize();
targetBucket = hashCode % buckets.Length;
}
index = count;
count++;
}

As you initialized the dictionary with capacity 2500, else clause seems not to called at all during such situation, so let's examine if part:

1. if (freeCount > 0) {
2. // atomic assign
3. index = freeList;
4. // some calculation and atomic assign
5. freeList = entries[index].next;
6. // not threadsafe operation
7. freeCount--;
8. }

Seems like we have multiple multi-threading issues here:

  1. freeList and freeCount fields aren't volatile, so read/write to it can be error-prone.
  2. What if billions of threads will be here right in this moment?: ©

    3. index = freeList;
    Billion of threads will get the same index, as there is no synchronization between read and write for the freeList field! And after that they'll override the value for each other with a race condition:

    entries[index].hashCode = hashCode;
    entries[index].next = buckets[targetBucket];
    entries[index].key = key;
    entries[index].value = value;
    buckets[targetBucket] = index;
    version++;
  3. decrement operation isn't thread-safe (great interesting answer from @EricLippert), so we'll have potentially corrupted state for the dictionary.
  4. What if billions of threads will be here right in this moment?: ©

    5. freeList = entries[index].next;
    Some thread gets the freeList value into index variable, say we have 5 there (the entries contains the linked list inside them, with head equal to -1) and became inactive before overriding the freeList. Billion of threads advances the linked list position after position, and now our first one became active. He happily overrides the freeList with obsolete data, creating a ghosts in linked list, and data there can be overridden.

So there is a lot of issues can happen during your code execution, and personally I don't recommend you to use Dictionary class in such circumstances.

Which members of .NET's ConcurrentDictionary are thread-safe?

Notice the section of the documentation that covers explicit interface implementations. E.g. the class implements IDictionary.Add. This method is not a public or protected member of the class, but may be accessed via the IDictionary interface. It is these such members that are not being guaranteed to be thread safe.

Thread safety of a DictionaryTKey, TValue with multiple concurrent readers and no writers

For your future reference, the documentation is here:

http://msdn.microsoft.com/en-us/library/xfhwa508.aspx

It says:

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.

ConcurrentDictionaryTKey,TValue vs DictionaryTKey,TValue

Without knowing more about what you're doing within the lock, then it's impossible to say.

For instance, if all of your dictionary access looks like this:

lock(lockObject)
{
foo = dict[key];
}

... // elsewhere

lock(lockObject)
{
dict[key] = foo;
}

Then you'll be fine switching it out (though you likely won't see any difference in performance, so if it ain't broke, don't fix it). However, if you're doing anything fancy within the lock block where you interact with the dictionary, then you'll have to make sure that the dictionary provides a single function that can accomplish what you're doing within the lock block, otherwise you'll end up with code that is functionally different from what you had before. The biggest thing to remember is that the dictionary only guarantees that concurrent calls to the dictionary are executed in a serial fashion; it can't handle cases where you have a single action in your code that interacts with the dictionary multiple times. Cases like that, when not accounted for by the ConcurrentDictionary, require your own concurrency control.

Thankfully, the ConcurrentDictionary provides some helper functions for more common multi-step operations like AddOrUpdate or GetOrAdd, but they can't cover every circumstance. If you find yourself having to work to shoehorn your logic into these functions, it may be better to handle your own concurrency.



Related Topics



Leave a reply



Submit