-2

I read code of AddOrUpdate method in Reference Source and saw that if oldValue and newValue are equaled the method will do the update anyway. Is it true? Did i understand it correct? Because of it the method always do lock.

update: When i saw the code of method AddOrUpdate i think "why we need use TryUpdate with lock if oldValue equals newValue? We can check equaling and return if values equals" Because of it i asked this question.

I understood why this method always do update or add, without checking equals of oldValue and newValue. TValue is object and we can't compare it without overwriting method equals.

Because of it, if you want AddOrUpdate without lock your ConcurrentDictionary when oldValue and newValue is equals, you need to create method with your classes. I don't know how you can do it using generic.

3 Answers3

0

You should use ContainsKey before the update.

ContainsKey calls TryGetValue which at line 509 reads:

 // We can get away w/out a lock here. 

:)

tymtam
  • 31,798
  • 8
  • 86
  • 126
-1

I think Possible can do that ? Use IEqualityComparer<TValue> to compare first.

public TValue AddOrUpdate<TArg>(TKey key, Func<TKey, TArg, TValue> addValueFactory, Func<TKey, TValue, TArg, TValue> updateValueFactory, TArg factoryArgument)
{
      // key exists, try to update
      if (TryGetValue(key, out oldValue))
      {

           TValue newValue = updateValueFactory(key, oldValue, factoryArgument);
           IEqualityComparer<TValue> valueComparer = EqualityComparer<TValue>.Default;
           if (valueComparer.Equals(oldValue, newValue))
           { return newValue; }
        }
        //...
    }
TimChang
  • 2,249
  • 13
  • 25
-2

I understood why this method always do update or add, without checking equals of oldValue and newValue. TValue is object and we can't compare it without overwriting method equals.

Because of it, if you want AddOrUpdate without lock your ConcurrentDictionary when oldValue and newValue is equals, you need to create method with your classes. I don't know how you can do it using generic.

  • 1
    The method will either _Add_ or _Update_. Both actions require a lock. Your question and your answer are unclear. – H H Oct 03 '19 at 05:53
  • This doesn't look as an answer but rather as part of the question. Please consider updating your question instead of spawning new posts which may confuse the readers in future. – Dmytro Mukalov Oct 03 '19 at 06:16
  • When i saw the code of method AddOrUpdate i think "why we need use TryUpdate with lock if oldValue equals newValue? We can check equaling and return if values equals" Because of it i asked this question. – Константин Золин Oct 03 '19 at 07:14
  • @КонстантинЗолин you can't or rather you can't do it safely. You'll have to make the entire process of value updating atomic (by wrapping it into `lock` for example) which will totally negate the benefits of `ConcurrentDictionary`. – Dmytro Mukalov Oct 03 '19 at 07:22
  • @DmytroMukalov for example, i have ConcurrentDictionary. Why I can't use it: if (TryGetValue(key, out oldValue)) { if (oldValue != newValue) TryUpdate(key, newValue, oldValue); } else { TryAdd(key, newValue) } . Why i need do it as atomic (into lock) ? I see only one problem, if it is generic method oldValue and newValue are objects and we can't equals it as int value. – Константин Золин Oct 03 '19 at 07:44
  • @КонстантинЗолин because this conditional update isn't atomic. In other words, if there are couple of such calls executed concurrently and the `oldValue` happens to be read by any call just before the moment when it has been modified by another call, the call which obtained this stale value will "think" that the values are equal while in reality they are not (and won't update the item with new value). – Dmytro Mukalov Oct 03 '19 at 08:15
  • @DmytroMukalov ok, i understand it. But what guarantees we that have when use AddOrUpdate method, that when we get value after updating, it will not changed by other TryUpdate method ? I am not good in English and have some problem in explaining my opinion. But we always check value or get value in some time, and we don't have guarantees that it will not change in future. Because of it does not matter will we check it before TryUpdate and returns or we use TryUpdate, and return value which not actual now, because was changed after updating. – Константин Золин Oct 03 '19 at 08:51
  • @КонстантинЗолин, the `ConcurrentDictionary` implementation of `AddOrUpdate` doesn't return a non-actual value, neither it makes a wrong assumption to return prematurely, so it always returns value which is set by the operation, i.e. the actual one. It always makes an update because of fundamental concurrency problem that no assumption can be made about a state unless it happens inside an atomic operation. And as soon as `ConcurrentDictionary` uses only fine-grained locks to achieve less contention, the freshness of the value isn't guaranteed outside of these atomic blocks. – Dmytro Mukalov Oct 03 '19 at 09:23
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/200355/discussion-between---and-dmytro-mukalov). – Константин Золин Oct 03 '19 at 15:50