Is ConcurrentDictionary.GetOrAdd() guaranteed to invoke valueFactoryMethod only once per key?
As others have already pointed out, valueFactory
may be invoked more than once. There is a common solution that mitigates this issue - have your valueFactory
return a Lazy<T>
instance. Although it's possible that multiple lazy instances will be created, the actual T
value will only be created when you access Lazy<T>.Value
property.
Specifically:
// Lazy instance may be created multiple times, but only one will actually be used.
// GetObjectFromRemoteServer will not be called here.
var lazyObject = dict.GetOrAdd("key", key => new Lazy<MyObject>(() => GetObjectFromRemoteServer()));
// Only here GetObjectFromRemoteServer() will be called.
// The next calls will not go to the server
var myObject = lazyObject.Value;
This method is further explained in Reed Copsey's blog post
Is value factory invoked only once per key?
No, it isn't. The docs say:
If you call
GetOrAdd
simultaneously on different threads, valueFactory may be invoked multiple times, but its key/value pair might not be added to the dictionary for every call.
Let's take a look at the source code of GetOrAdd
:
public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
{
if (key == null) throw new ArgumentNullException("key");
if (valueFactory == null) throw new ArgumentNullException("valueFactory");
TValue resultingValue;
if (TryGetValue(key, out resultingValue))
{
return resultingValue;
}
TryAddInternal(key, valueFactory(key), false, true, out resultingValue);
return resultingValue;
}
Unfortunately, in this case, it's clear nothing guarantees that valueFactory
won't be called more than once, if two GetOrAdd
invocations happen to run in parallel.