Are IDictionary elements implemented in a ConcurrentDictionary stream safe?

When calling DoStuffToDictionary (dictionaryTwo), is it safe to accept operations inside the method body, including indexers, and will LINQ extension methods be thread safe?

To formulate this question in a different way, can cross-stream exception or deadlock occur?

var dictionaryOne = new ConcurrentDictionary<int,int>(); var dictionaryTwo = new Dictionary<int,int>(); DoStuffToDictionary(dictionaryOne); DoStuffToDictionary(dictionaryTwo); void DoStuffToDictionary(IDictionary<int,int> items) { // Manipulate dictionary if (items[0] == -1) { items[0] = 0; // Dumb example, but are indexers like this OK? } } 
+6
source share
3 answers

There are several problems with this code:

  • IDictionary interface can be implemented with any type of dictionary
    Your example, of course, is not thread safe, since you are working with the IDictionary<int,int> interface, which does not guarantee thread safety. Even your code passes Dictionary and ConcurrentDictionary to the method.

  • Transactions must be atomic in order to make them thread safe.
    Even if the implementation of the dictionary would guarantee thread safety, your code would not be because you are not blocking access to your dictionary between two calls :

     if (items[0] == -1) { // <-- another thread can access items in this moment items[0] = 0; } 
  • LINQ query return is never thread safe
    If you use LINQ to return IEnumerable or IQueriable from your method, then locks have little effect unless you use the ToList() method to evaluate the expression directly and the cache results. This is because LINQ only β€œprepares” the query for execution. If you return the IEnumerable method from the method, access to the actual dictionary will be after , your method will end (and, therefore, outside the lock).

The biggest problem with this code is that you pass an instance of IDictionary around , which means that other parts of your code can access it directly and must block on the same instance of the lock object very carefully. It is painful, error-prone for proper implementation, easily broken by accident and difficult to detect (race conditions may show symptoms in rare cases).

You can make several code improvements:

  • Do not skip IDictionary , but instead your own interface (preferred)
    Make the dictionary a private member of a class that implements some kind of user interface, abstracts all operations and uses locking to ensure thread safety (or use ConcurrentDictionary under the hood). This way, you are sure that all calls are blocked using the same lock instance.

  • Do not use an interface, but rather pass a ConcurrentDictionary
    This will be thread safe if you use the special atomic methods that ConcurrentDictionary provides ( GetOrAdd , AddOrUpdate , etc.). Using simple access methods, as you did in your example, will not be thread safe, which means that you still need to be careful with it. An additional drawback is that you cannot abstract from functionality if you ever need it (it will be impossible to move / proxy the dictionary, and you cannot use other implementations of the dictionary).

  • Skip the IDictionary around and lock the dictionary itself (not recommended at all).
    This is an ugly hack that, unfortunately, is used more often than it should be. This means that you need to do this in every part of your application that accesses this dictionary, paying particular attention to blocking several operations along the way.

+7
source

1) For Dictionary Extension methods, LINQ are not thread safe, because the class itself is not thread safe, but for ConcurrentDictionary it is thread safe and therefore calls LINQ extension functions for it is thread safe.

Edit:

2) Is it safe to assume the operations within the method body including indexers I don't know what you mean by including indexers , but if you mean that the dictionaries are not empty. then for Dictionary you should not consider that it contains elements .. however for ConcurrentDictionary you can use methods such as: AddOrUpdate();, GetOrAdd();, TryAdd();, TryGetValue();

+1
source

lines (together)

  if (items[0] == -1) { items[0] = 0; // Dumb example, but are indexers like this OK? 

Are NOT thread safe ... with ConcurrentDictionary this

  ConcurrentDictionary<int, int> D = new ConcurrentDictionary<int, int>(); D.TryUpdate(0, 0, -1); // this is threadsade regarding a ConcurrentDictionary 

will be thread safe with the result you want to achieve with the lines above.

+1
source

Source: https://habr.com/ru/post/893446/


All Articles