Is ConcurrentDictionary.GetOrAdd() guaranteed to invoke valueFactoryMethod only once per key?

前端 未结 3 1713
爱一瞬间的悲伤
爱一瞬间的悲伤 2021-01-07 16:29

Problem: I need to implement object cache. The cache need to be thread-safe and need to populate values on demand(lazy loading). The values are retrieved vi

相关标签:
3条回答
  • 2021-01-07 16:48

    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

    0 讨论(0)
  • 2021-01-07 16:51

    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.

    0 讨论(0)
  • 2021-01-07 17:02

    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.

    0 讨论(0)
提交回复
热议问题