2

Why do we need third argument comparisonValue in ConcurrentDictionary.TryUpdate method?

And why will updating not succeed if already existed value is not equal to comparisonValue? Can't we just replace existed value with the new one just like in normal Dictionary<,>?

This is the signature:

public bool TryUpdate(TKey key, TValue newValue, TValue comparisonValue)
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Wachburn
  • 2,842
  • 5
  • 36
  • 59
  • 1
    The whole point of this method is to expose [optimistic locking](https://stackoverflow.com/questions/129329/optimistic-vs-pessimistic-locking). You can still use `IDictionary`'s [`Item[]`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.idictionary-2.item?view=net-6.0) operator as [`ConcurrentDictionary`](https://learn.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentdictionary-2?view=net-6.0) implements it. – orhtej2 Feb 02 '22 at 16:17
  • 1
    What are you trying to do? – Luaan Feb 02 '22 at 16:21
  • 1
    @TheodorZoulias if you see any improvements, welcome – Wachburn Feb 02 '22 at 16:49

2 Answers2

2

The point is that you're using concurrent dictionary for scenarios with concurrent access to the dictionary. You don't know who (and how) changed the dictionary in the meantime. Passing a comparison value is a very simple and effective way of only doing the change if the state of the dictionary is the same one you expect.

If you expect collisions to be relatively rare, this is a very efficient and performant way of handling shared state (no need for locking, and thus stopping all access). This pattern is the basis of lock-free code; you see it even on the hardware level. You can look up Compare and Exchange (or Compare and Swap) for more information.

Luaan
  • 62,244
  • 7
  • 97
  • 116
  • What do you mean by lock-free code? I don't see any conditions in which the lock statement will be skipped in TryUpdate source code. And CompareExchange is designed to replace multiple constructions, but in atomic way, if you don't want that logic you can use simple Interlocked.Exchange. TryUpdate is mocking CompareExchange behavior while there are no Exchange analog for dictionary – Wachburn Feb 02 '22 at 17:30
  • @Wachburn You can always do `dictionary[key] = value;`. – Luaan Feb 03 '22 at 08:32
1

If you want to update a key of a ConcurrentDictionary regardless of its current value, you can just use the set accessor of the indexer:

var dictionary = new ConcurrentDictionary<int, string>();
dictionary[1] = "Hello";
dictionary[2] = "World";
dictionary[1] = "Goodbye";
Console.WriteLine(String.Join(", ", dictionary));

Output:

[1, Goodbye], [2, World]

If each thread is working with an isolated set of keys, updating a ConcurrentDictionary like this might be sufficient. But if multiple threads are competing for updating the same keys, chaos might ensue. In those cases it might be desirable to use the TryUpdate method, or more frequently the AddOrUpdate method. These methods allow to update conditionally the dictionary, with the checking and updating being an atomic operation.

The following question might offer some insights about how this API can be used in practice:
Is there a way to use ConcurrentDictionary.TryUpdate with a lambda expression?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104