Dictionary Lock TryGetValue () - Performance Issues

I profiled my application and performed some performance tests, which led me to think that the following if-lock-if scheme:

private float GetValue(int id) { float value; if (!dictionary.TryGetValue(id, out value)) { lock (lockObj) { if (!dictionary.TryGetValue(id, out value)) { value = ComputeValue(id); dictionary.Add(id, value); } } } } 

seems to be faster than "lock-if" or using ReaderWriterLockSlim. But very rarely, I get the following exception:

 1) Exception Information ********************************************* Exception Type: System.NullReferenceException Message: Object reference not set to an instance of an object. Data: System.Collections.ListDictionaryInternal TargetSite: Int32 FindEntry(TKey) HelpLink: NULL Source: mscorlib StackTrace Information ********************************************* at System.Collections.Generic.Dictionary`2.FindEntry(TKey key) at System.Collections.Generic.Dictionary`2.TryGetValue(TKey key, TValue& value) at MyNamespace.GetValue() ..... ..... 

What am I doing wrong here?

Change To clarify, this method is called on average more than 50 million times, and the conflict is usually less than 5000.

thanks

+6
source share
4 answers

What you are trying to do here is simply not supported. TryGetValue occurs outside of the lock, which means that it is very important that one thread writes to the dictionary, while others call TryGetValue at the same time. The only streaming scenario that is essentially supported by Dictionary<TKey, TValue> is read from multiple streams. After you start reading and writing from multiple threads, all bets are disabled.

To make it safe, you must do one of the following

  • Use one lock for all read or write requests to the Dictionary
  • Use a type of type ConcurrentDictionary<TKey, TValue> , which is designed for multi-threaded scripts.
+15
source

Either the use of this collection by your code is thread safe, in which case you do not need a lock, or it is not thread safe, in which case you should ALWAYS block.

Try using ConcurrentDictionary , which is thread safe, instead.

+3
source

Dictionary not thread safe. If something adds a dictionary when you do TryGetValue , things might go wrong. Your first call to TryGetValue not locked. Therefore, if thread A performs Add , and thread B enters the first TryGetValue , it may throw an exception.

Consider using System.Collections.Concurrent.ConcurrentDictionary . Or be sure to lock the dictionary with every access. It is possible to use ReaderWriterLockSlim .

+2
source

I think that removing the first if that does not respond to the thread should solve the problem.

Is there a reason for repeating the same if ?

0
source

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


All Articles