Little-known gems: Atomic conditional removals from ConcurrentDictionary

Little-known gems: Atomic conditional removals from ConcurrentDictionary

  • Comments 6

ConcurrentDictionary<TKey,TValue>, first introduced in .NET 4, is an efficient dictionary data structure that enables thread-safe reading and writing, meaning that multiple threads may all be accessing the dictionary at the same time without corrupting it.  It supports adding through its TryAdd method, conditional updates through its TryUpdate method, non-conditional adds or updates through its indexer's setter, and removals through its TryRemove method, through which a key/value pair is removed if the user-provided key matches.  It also has several compound methods, such as GetOrAdd and AddOrUpdate.  Lately, however, we've seen several folks ask for further support on ConcurrentDictionary, that of removing a key/value pair only if both the user-provided key and value match the corresponding pair currently stored in the dictionary. 

You want it, you got it!  I mean, literally, you already have it.  As with Dictionary<TKey,TValue>, ConcurrentDictionary<TKey,TValue> implements ICollection<KeyValuePair<TKey,TValue>>. ICollection<T> exposes a Remove<T> method, so ICollection<KeyValuePair<TKey,TValue>> has a Remove(KeyValuePair<TKey,TValue>) method.  Dictionary<TKey,TValue> implements Remove such that it only removes the element if both the key and the value match the data in the dictionary, and thus ConcurrentDictionary<TKey,TValue> does the same.  And as it's a concurrent data structure, ConcurrentDictionary<TKey,TValue> ensures that the comparison is done atomically with the removal.  Tada!

Of course, just as with Dictionary<TKey,TValue>, ConcurrentDictionary<TKey,TValue> implements ICollection<KeyValuePair<TKey,TValue>> explicitly, so if you want access to this method, you'll need to first cast the dictionary to the interface.  Here's an extension method to help you with that cause:

public static bool TryRemove<TKey, TValue>(
    this ConcurrentDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
    if (dictionary == null) throw new ArgumentNullException("dictionary");
    return ((ICollection<KeyValuePair<TKey, TValue>>)dictionary).Remove(
        new KeyValuePair<TKey, TValue>(key, value));
}

Enjoy!

Leave a Comment
  • Please add 1 and 5 and type the answer here:
  • Post
  • Great post.

    Thx Steve

  • thank you, this sure helps :)

  • We could never have figured out how to make a concurrent dictionary ourselves. Thank you Microsoft.

  • One thing I'm wondering is why is IDictionary<TKey, TValue>.Remove(TKey) implemented explicitly? Why doesn't it work they way it does with Dictionary? I.e. Dictionary implements the generic Remove() method the normal way and implements the non-generic one explicitly. ConcurrentDictionary implements them both explicitly. Unless I'm missing something, this means you have to cast in order to call it.

  • Jon, thanks for the question.

    re: "Why doesn't it work they way it does with Dictionary?"

    Because they're two different types designed for two different sets of scenarios.  In a concurrent world, we saw most of the use cases being for situations where you knew you wanted to remove a particular key, regardless of the value.  That's what the public TryRemove enables.  We of course still wanted to enable ConcurrentDictionary<,> to be used as an IDictionary<,> and to support everything it supports, but we didn't want to confuse the public API of folks directly using ConcurrentDictionary<,>.  If you only care about the underlying interfaces, you can just make your variables of that type, and then you'll see the facade you're looking for.

  • Thanks for your explanation, Stephen!

    But regarding your comment that "we saw most of the use cases..."  It's understandable, and you might be right, but isn't that suggesting that ALL classes should ONLY contain methods to support "most of the use cases?"

    Thanks!

Page 1 of 1 (6 items)