Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConcurrentDictionary.GetOrAdd Always Executes Delegate Method

I'm noticing that GetOrAdd() always executes the factory delegate, even when the value exists in the dictionary. For example:

class Program {     private static ConcurrentDictionary<string, string> _cache = new ConcurrentDictionary<string, string>();      static void Main(string[] args)     {         string value;          value = GetValueFromCache("A"); // cache is empty, CacheValueFactory executes, A is added         value = GetValueFromCache("A"); // cache contains A, CacheValueFactory executes         value = GetValueFromCache("C"); // cache contains A, CacheValueFactory, C is added         value = GetValueFromCache("A"); // cache contains A and C, CacheValueFactory executes     }      private static string GetValueFromCache(string key)     {         string val = _cache.GetOrAdd(key, CacheValueFactory(key));          return val;     }      private static string CacheValueFactory(string key)     {         if (key == "A")             return "Apple";         else if (key == "B")             return "Banana";         else if (key == "C")             return "Cherry";          return null;     } } 

Upon the first call to GetValueFromCache("A"), the cache is empty and A:Apple is added. Stepping in with the debugger, I noticed that on the second and third calls to GetValueFromCache("A"), the CacheValueFactory() method always executes. Is this expected? I would've thought that the delegate method wouldn't execute if the key exists in the dictionary.

like image 223
Bullines Avatar asked Dec 09 '10 16:12

Bullines


People also ask

Is ConcurrentDictionary AddOrUpdate thread safe?

It is thread safe in your usage. It becomes not thread safe when the delegate passed to AddOrUpdate has side effects, because those side effects may be executed twice for the same key and existing value.

Is ConcurrentDictionary GetOrAdd thread safe?

The GetOrAdd functionThe vast majority of methods it exposes are thread safe, with the notable exception of one of the GetOrAdd overloads: TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory); This overload takes a key value, and checks whether the key already exists in the database.

How ConcurrentDictionary works c#?

ConcurrentDictionary is thread-safe collection class to store key/value pairs. It internally uses locking to provide you a thread-safe class. It provides different methods as compared to Dictionary class. We can use TryAdd, TryUpdate, TryRemove, and TryGetValue to do CRUD operations on ConcurrentDictionary.

How to make a concurrentdictionary only call a delegate once when using getoradd?

tl;dr; To make a ConcurrentDictionary only call a delegate once when using GetOrAdd, store your values as Lazy<T>, and use by calling GetOrAdd (key, valueFactory).Value. The ConcurrentDictionary is a dictionary that allows you to add, fetch and remove items in a thread-safe way .aspx).

Is getoradd atomic in concurrentdictionary?

(Read operations on the dictionary are performed in a lock-free manner.) However, the valueFactory delegate is called outside the locks to avoid the problems that can arise from executing unknown code under a lock. Therefore, GetOrAdd is not atomic with regards to all other operations on the ConcurrentDictionary<TKey,TValue> class.

Is concurrentdictionary thread safe?

The ConcurrentDictionary<T,V> in .NET 4.0 is thread safe but not all methods are atomic. ... not all methods are atomic, specifically GetOrAdd and AddOrUpdate. The user delegate that is passed to these methods is invoked outside of the dictionary's internal lock.

How does concurrentdictionary<TKey> work?

While valueFactory is generating the value, a different thread inserts a value for the key. After valueFactory executes and upon rechecking for the key, the key inserted by the other thread is found. The value inserted by the other thread is returned. Adds a key/value pair to the ConcurrentDictionary<TKey,TValue> if the key does not already exist.


Video Answer


2 Answers

The reason you're seeing this is that you are not passing CacheValueFactory as a delegate but instead evaluating the function promptly and passing the resulting value. This causes you to use the overload which accepts a key and value and not the one which accepts a key and delegate.

To use the delegate version switch the code to the following

string val = _cache.GetOrAdd(key, CacheValueFactory); 
like image 122
JaredPar Avatar answered Oct 08 '22 20:10

JaredPar


If you want to handle slightly more complicated scenarios, for example, when the parameter doesn't match the key, you could use a lambda.

        var keyStr = string.Format("Something_{0}", key);         string val = _cache.GetOrAdd(keyStr,_ => CacheValueFactory(key)); 
like image 43
JNappi Avatar answered Oct 08 '22 18:10

JNappi