1

If I am using a collection from the System.Collection.Concurrent namespace, such as ConcurrentDictionary<K, V>, with a key and/or value type that is not threadsafe, for instance ConcurrentDictionary<int, Dictionary<int, int>> What are the possible issues faced?

I assume that I can do any operation that I please on the ConcurrentDictionary itself, but what if I create a System.Collections.Generic.List<T> of the Dictionary<int, int> from that ConcurrentDictionary and modify it?

Here I create the list with LINQ in "one line" though I assume that once the ToList is executed I am out of the safety of the ConcurrentDictionary lock?

ConcurrentDictionary<int, Dictionary<int, int>> conDict = ...;
conDict.Select(x => x.Value).ToList().ForEach(x => x.Add(1, 2));

If that is safe or unsafe I then assume this is too

ConcurrentDictionary<int, Dictionary<int, int>> conDict = ...;
var regDictList = conDict.Select(x => x.Value).ToList();
regDictList.ForEach(x => x.Add(1, 2));
Tamir Vered
  • 10,187
  • 5
  • 45
  • 57
KDecker
  • 6,928
  • 8
  • 40
  • 81

1 Answers1

4

Well, the ConcurrentDictionary is by itself self for access from multiple threads (TryAdd, TryGetValue, Etc...).

It's important to understand that it's not the objects contained by the ConcurrentDictionary that are thread-safe but the ConcurrentDictionary itself.


If you access a specific value contained by the dictionary from multiple threads than you have to make sure it is thread-safe as well.

Since the result from:

conDict.Select(x => x.Value)

is:

IEnumerable<Dictionary<int, int>>

and it no longer has anything to do with the ConcurrentDictionary - a List<Dictionary<int, int>> retrieved by:

conDict.Select(x => x.Value).ToList()

is of course NOT thread-safe

Tamir Vered
  • 10,187
  • 5
  • 45
  • 57
  • So in my example above if I would like to keep the `conDict.Values` thread safe I would need to change the type to `ConcurrentDictionary>`? – KDecker Aug 26 '16 at 17:38
  • @KDecker Yes, but note that you shouldn't modify the outer `ConcurrentDictionary` from another thread while iterating over its values ([Like any collection is not allowed to be modified while being iterated](http://stackoverflow.com/questions/7761959/collection-was-modified-enumeration-operation-may-not-execute-c-sharp)). – Tamir Vered Aug 26 '16 at 17:41
  • 1
    Every time you modify ***a specific object*** from multiple threads, make sure that ***that specific object*** is thread-safe. – Tamir Vered Aug 26 '16 at 17:43
  • @TamirVered your warning about modifying the collection does not apply to the classes in the `System.Collections.Concurrent` namespace, all of the collections in that namespace take a "snapshot" of the collection and iterate over that snapshot. Basically any time you do a `foreach(var foo in myConcurrentDictionary)` internally it is doing something similar to you doing `foreach(var foo in myConcurrentDictionary.ToList())` – Scott Chamberlain Aug 26 '16 at 17:51
  • @Scott Linq is lazy, it iterates over the original collection until `.ToList` or something like that (which iterates the `IEnumerable`) is called – Tamir Vered Aug 26 '16 at 17:54
  • That's why I said "*something similar to you doing*", internally it is much more complicated, I was just trying to point out that you will never get the error you linked to in your comment from a `System.Collections.Concurrent` collection – Scott Chamberlain Aug 26 '16 at 17:56
  • 1
    Actually, I was kinda wrong, it does not do a snapshot, however it is still safe to modify the outer dictionary while enumerating, [the MSDN specifically states that it is safe to do so.](https://msdn.microsoft.com/en-us/library/dd287131(v=vs.110).aspx#Anchor_1) – Scott Chamberlain Aug 26 '16 at 18:00
  • Here is a webpage I was just reading that I think might be related to this sub-discussion Tamir and Scott (https://arbel.net/2013/02/03/best-practices-for-using-concurrentdictionary/). It discusses the "snapshot mechanic". – KDecker Aug 26 '16 at 18:29