Extension methods to make ConcurrentDictionary GetOrAdd and AddOrUpdate thread safe when using valueFactory delegates The ConcurrentDictionary&lt;T,V&gt; in .NET 4.0 is thread safe but not all methods are atomic.

This points out that:


  ... 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.


Example Problem:

It is possible for the delegate method to be executed more than once for a given key.

public static readonly ConcurrentDictionary&lt;int, string&gt; store =
    new ConcurrentDictionary&lt;int, string&gt;();

[TestMethod]
public void UnsafeConcurrentDictionaryTest()
{

    Thread t1 = new Thread(() =&gt;
    {
        store.GetOrAdd(0, i =&gt;
        {
            string msg = "Hello from t1";
            Trace.WriteLine(msg);
            Thread.SpinWait(10000);
            return msg;
        });
    });

    Thread t2 = new Thread(() =&gt;
    {
        store.GetOrAdd(0, i =&gt;
        {
            string msg = "Hello from t2";
            Trace.WriteLine(msg);
            Thread.SpinWait(10000);
            return msg;
        });
    });

    t1.Start();
    t2.Start();
    t1.Join();
    t2.Join();
}


The result shown in the Trace window shows "Hello from t1" and "Hello from t2". This is NOT the desired behavior for most implementations that we are using and confirms the problem noted in the MSDN link above. What we want is for only one of those delegates to be executed.

Proposed Solution:

I have to use the delegate overloads of these methods which led me to investigate this matter further. I stumbled onto this post which suggests using the Lazy&lt;T&gt; class to ensure the delegate is only invoked once. With that in mind I wrote the following extension methods to mask the adding of a Lazy&lt;T&gt; wrapper to the value.

public static V GetOrAdd&lt;T, U, V&gt;(this ConcurrentDictionary&lt;T, U&gt; dictionary, T key, Func&lt;T, V&gt; valueFactory)
where U : Lazy&lt;V&gt;
{
    U lazy = dictionary.GetOrAdd(key, (U)new Lazy&lt;V&gt;(() =&gt; valueFactory(key)));
    return lazy.Value;
}

public static V AddOrUpdate&lt;T, U, V&gt;(this ConcurrentDictionary&lt;T, U&gt; dictionary, T key, Func&lt;T, V&gt; addValueFactory, Func&lt;T, V, V&gt; updateValueFactory)
where U : Lazy&lt;V&gt;
{
    U lazy = dictionary.AddOrUpdate(key, 
                (U)new Lazy&lt;V&gt;(() =&gt; addValueFactory(key)), 
                (k, oldValue) =&gt; (U)new Lazy&lt;V&gt;(() =&gt; updateValueFactory(k, oldValue.Value)));
    return lazy.Value;
}


Testing Solution:

Executing the same test above using a ConcurrentDictionary that has a Lazy value results in the value delegate ONLY being executed once (you either see "Hello from t1" or "Hello from t2")! 

public static readonly ConcurrentDictionary&lt;int, Lazy&lt;string&gt;&gt; safeStore =
            new ConcurrentDictionary&lt;int, Lazy&lt;string&gt;&gt;();


So it seems that this approach accomplished the goal.

What do you think of this approach?
Allowing the caller to provide the type argument U implies that they are allowed to use a subclass of Lazy&lt;V&gt;, but this will not work as your implementations always creates a new List&lt;V&gt; and cast it to U. Since this means U must always be a Lazy&lt;V&gt; then why not do away with the extra type argument.

public static V GetOrAdd&lt;T, V&gt;(this ConcurrentDictionary&lt;T, Lazy&lt;V&gt;&gt; dictionary, T key, Func&lt;T, V&gt; valueFactory)

The name of the new extension methods conflict with the names of the existing methods. For the consumer to use yours instead of the existing methods, they would need to either access via your static class or use explicit type arguments. This could lead to subtle bugs when consumers try to use it as a extension method with type inference.

ExtensionHost.GetOrAdd(safeStore, 7, (i) =&gt; i.ToString());             // uses yours
safeStore.GetOrAdd&lt;int, Lazy&lt;string&gt;, string&gt;(6, (i) =&gt; i.ToString()); // uses yours
safeStore.GetOrAdd(5, (i) =&gt; i.ToString());                            // uses existing