Introduction

The goal of this article is to show the problem of race condition while execution of GetOrAdd(TKey, Func<TKey, TValue>) method. The described problem has a few solutions. I will focus on one I found at StackOverflow, and I will explain why this solution works.

Double execution problem

Not everyone realizes that  GetOrAdd(TKey, Func<TKey, TValue>) can call multiple times the delegate resolving value for a single key. It is easy to prove this thesis with the following code.

//// This counter is incremented while value resolving delegate is called.
int executionCounter = 0;
var dictionaryUnderTest = new ConcurrentDictionary&lt;int, int&gt;();

var rootSynchronizationPoint = new SemaphoreSlim(0);
var addSynchronizationPoint = new SemaphoreSlim(0);

Action newKeyResolve = () =&gt; dictionaryUnderTest.GetOrAdd(1, (key) =&gt;
{
    //// Here value resolver informs root thread that delegate execution is started
    rootSynchronizationPoint.Release();
    //// Now we are waiting when root thread realize that both resolvers are started
    addSynchronizationPoint.Wait();
    int counter = Interlocked.Increment(ref executionCounter);
    return counter * 100 + key;
});

//// Executing resolvers in different thread. 
//// NOTE!!! Execution MUST be multi threaded. 
//// Thats why class Thread instead of class Task is used. 
Thread t1 = new Thread(new ThreadStart(newKeyResolve));
Thread t2 = new Thread(new ThreadStart(newKeyResolve));
t1.Start();
t2.Start();

//// Waiting for both resolvers to be started
rootSynchronizationPoint.Wait();
rootSynchronizationPoint.Wait();
//// Waiting for both resolvers to be started
addSynchronizationPoint.Release(2);
t1.Join();
t2.Join();

//// Waiting for both resolvers to be started
executionCounter.Should().Be(2);

The only guarantee delivered by ConcurrentDictionary is that first resolved value will be stored in the dictionary and returned to all callers of GetOrAdd method. It means both callers will receive identical/single value – second resolved result is thrown away. We could stop here, but what if we want to have guarantee, that resolver is called only once? We can use the trick from StackOverflow. Instead of using TValue as a result, we can simply use Lazy<T> class as a wrapper for our value.  It is explained very nice in Reed Copsey, Jr. article http://reedcopsey.com/2011/01/16/concurrentdictionarytkeytvalue-used-with-lazyt/.

NOTE! You have to remember that Lazy<T> uses lock, to resolve value. So when you are using Task-based Asynchronous Programming your resolver should return Lazy<Task<TValue>>> to be on safe side.

If you want to experiment with the source code, I added my test project on GitHub here.