Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ConcurrentDictionary + Lazy -- would instantiation happen only once?

Scenario

Let's say we have:

var dictionary = new ConcurrentDictionary<string, Lazy<Heavy>>();

Instantiating Heavy is very resource-consuming. Let's consider this code:

return dictionary.GetOrAdd("key", key => 
{
    return new Lazy<Heavy>(() =>
    {
        return Instantiate();
    });
}).Value;

The method Instantiate() of course returns an instance of type Heavy.

Question

For a given key, is it 100% guaranteed that the method Instantiate() will be invoked at most once?

Sources

Some people claim that having multiple threads, we can only create multiple instances of Lazy<Heavy>, which is very cheap. The actual method Instantiate() will be invoked at most once.

  • https://social.msdn.microsoft.com/Forums/en-US/e350f7d0-b860-482e-9b84-8dba12267d25/failure-of-lock-with-tpl?forum=parallelextensions
  • http://reedcopsey.com/2011/01/16/concurrentdictionarytkeytvalue-used-with-lazyt/

I personally have an impression that this is false. What is the truth?

like image 885
Patryk Golebiowski Avatar asked Mar 08 '23 12:03

Patryk Golebiowski


1 Answers

Instantiate will indeed will execute only once. Documentation of GetOrAdd says:

If you call GetOrAdd simultaneously on different threads, addValueFactory may be called multiple times, but its key/value pair might not be added to the dictionary for every call.

What that means is: even if addValueFactory is run multiple times - only one of returned values will actually be added to dictionary and returned from GetOrAdd call. So if two threads call GetOrAdd at the same time with the same key - 2 Lazy<Heavy> instances are created, but only 1 instance is added to dictionary and returned from both GetOrAdd calls, the other is discarded (so, even if factory has been run - it does not mean value provided by this factory is what is finally returned from GetOrAdd). Because you call .Value on the result of GetOrAdd - you call that always on single instance of Lazy<Heavy> and so Instantiate always runs at most once.

like image 146
Evk Avatar answered Apr 14 '23 06:04

Evk