Thargy.com

Blogs and articles by Craig Dean

The hidden secret of the Concurrent Dictionary

When it comes to my favourite ‘new*’ classes in the .NET BCL, one of my top ten is definitely the ConcurrentDictionary .  So much so, that (after IEnumerable) it’s probably one of the classes I go back to most frequently to solve any particular problem (particularly caches).

I still remember the frustration and failure I felt when I realised my own lovingly crafted solution to the problem of concurrent dictionaries was so enfeebled (particularly in performance, but also design) by this truly beautifully crafted class, and it didn’t take me long before I turned the power of reflection onto the class to unlock it’s dirty little secrets.  This is one of those gems that was written by the smarter guys at Microsoft, trust me they didn’t write everything in the BCL!

So it was with huge surprise that, a month or two ago, this Wunder-Klasse revealed another little beauty from behind its polished exterior.  The concurrent dictionary is fantastic for storing singular values in, but what happen when I want to store something more complex, in particular, an enumeration?  My solution was to create a ConcurrentLookup that implements ILookup.  I’d like to revisit the solution in a later post, but for now, let’s talk about what I discovered…

You see, ultimately the ‘standard’ public methods of the ConcurrentDictionary only get you so far before you come up against a big problem.  When removing a key, how do I make sure it only get’s removed if the value is what I think it is?

Well, the answer (in brief) is best illustrated by some example code –

// Create the dictionary
ConcurrentDictionary<int, int> dictionary = new ConcurrentDictionary<int, int>();

// Add [1, 2] and [2, 3]
dictionary.GetOrAdd(1, 2);
dictionary.GetOrAdd(2, 3);

// This will succeed
Assert.IsTrue(((ICollection<KeyValuePair<int, int>>) dictionary)
.Remove(new KeyValuePair<int, int>(1, 2)));

// Let's get the value from 2 and confirm it's 3.
int value;
dictionary.TryGetValue(2, out value);

Assert.AreEqual(value, 3);

// Meanwhile, imagine this runs on another thread...
dictionary.AddOrUpdate(2, 4, (k, v) => 4);

// This remove will fail as value still equals 3, but the dictionary now contains 4!
Assert.IsFalse(((ICollection<KeyValuePair<int, int>>)dictionary)
.Remove(new KeyValuePair<int, int>(2, value)));

// The dictionary keeps it's key nice and safe.
Assert.IsTrue(dictionary.ContainsKey(2));

This little horror shows that by casting the dictionary to one of its interfaces you can access an underused interface method explicitly.

I’ve known from day zero that ConcurrentDictionary implements the ICollection interface for the corresponding KeyValuePair, but it wasn’t until I was snooping around inside the source code that I stumbled across the private TryRemoveInternal  method, that had a mysterious matchValue  parameter.  It was a short hop to putting two and two together.  The method is technically in MSDN, but it’s not something you’d spot and the implications are not particularly obvious.  The fact that the designers chose to implement the method explicitly so that it wouldn’t be accessible without casting, is normally a hint that it is not a good idea to use it.  In fact, the BCL is littered with examples where such methods throw an exception, however, this one is designed to work as advertised, but you’d never know if Microsoft didn’t publish the source so readily.

Just another good reason to spend a bit of time digging deeper.

I hope this little gem solves a problem for you today if it does, let me know!

* ‘New’ as in it wasn’t in the first version of .NET I ever used!

Related Images:

, , , , , ,

Comments are currently closed.