Are nested Dictionaries thread safe from parent ConcurrentDictionary?

Viewed 117

If I have a ConcurrentDictionary object ConcurrentDictionary<int, Dictionary<string, string>>() dict;, is the nested Dictionary locked when operations are being performed on the outer ConcurrentDictionary?

Scenario: an outer ConcurrentDictionary outerDict is performing an

outerDict.Add(42, new Dictionary<string, string>())

on one thread, and, on another thread (simultaneously), an inner Dictionary is performing an

outerDict[30].Add("hello", "world").

Is concurrency applied to the modification of both the outer ConcurrentDictionary and the nested Dictionary in the above scenario, or are both operations performed at the same time?

3 Answers

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.

There are 4 operations here:

  • creating a new object. Doing that has no concurrency impact, since nobody else has a reference to the object at that time.
  • adding the object to the concurrent dictionary. No problem, because it's a concurrent dictionary.
  • accessing the 30th entry in the concurrent dictionary. No problem, because it's a concurrent dictionary
  • adding a new value to that 30th dictionary: problematic, if another thread has a reference to the 30th dictionary as well.

A ConcurrentDictionary<TKey, TValue> is thread safe. It's designed so that multiple threads can safely work with the keys in that dictionary. It cannot and does not extend that thread safety to the values stored in the dictionary.

The concurrent dictionary stores a reference to the inner dictionary. But the inner dictionary doesn't "know" about anything that references it. As far as it knows it's just a Dictionary<string, string>.

Although the concurrent dictionary has a reference to the inner dictionary, it doesn't in any way "own" or control that inner dictionary.

You could write this code:

var innerDictionary = new Dictionary<string, string>();
outerDict.Add(42, innerDictionary);

In this case innerDictionary still maintains a reference to that dictionary. It can pass it as an argument to another method. The concurrent dictionary could go out of scope and get garbage collected while some other object maintains a reference to the inner dictionary.

The point is that other than the concurrent dictionary holding a reference to an object, it doesn't otherwise control the behavior of that object.

Related