Friday, January 18, 2008 10:14 AM
Brian Rudolph
Thread Safe Dictionary in .NET with ReaderWriterLockSlim
Since MS has decided to not include a thread-safe dictionary in the .NET framework, many of us have been forced to implement our own. What MS has done now with .NET 3.5 is given us a newer locking mechanism which makes this task easier.
Previously, we had 2 lock mechanisms, neither of which worked very well for my uses. The first is the Lock statement. While this method is effective, it causes serious contention issues with simultaneous reads\writes. Since it is generally safe to execute multiple concurrent reads from a dictionary, this was a huge performance flaw as the Lock statement was forcing us to perform all actions against the object in a serial fashion.
So that left ReaderWriterLock, which would allow us to take out Reader & Writer locks independently. This seemed to be the answer. Unfortunately, after further testing and review, MS and the dev communities warned against this lock type as it could have unintended consequences in certain situations. The explanations for these I will not go in to, a simple Google search will answer those questions. For the most part, I don't have the mental capacity to understand, much less explain the failures. I will tell you that I have tested this method, and ran in to some issues, and subsequently abandoned the approach.
With .NET 3.5 comes the brand-spankin-new ReaderWriterLockSlim. ReaderWriterLockSlim is similar to the ReaderWriterLock in functionality, minus some of the drawbacks. Of course any new approach will bring on its own consequences, and if you do a little research, you will find that Slim isn't infallible either.
Many of our applications implement the Factory model. Factory models are great, they are extensible, and they can be extremely performant. With that said, any data driven application that uses the Factory model without a caching model, will most likely perform poorly. So, in our factories, we build in a caching mechanism. A very simple type and ID based cache that allows us to persist commonly used objects to in-memory cache for a specific amount of time. I won't go in to the implementation of this model here as every caching implementation has its own needs and wants.
At the heart of this and likely any caching model is inevitably a Dictionary or Dictionary like structure. MS programmers have spent countless hours fine-tuning the Dictionary class to make it as performant as possible. I challenge anyone to write a managed Dictionary that performs as well in such a wide variety of scenarios. I've tried and failed. Unfortunately, MS has not provided a thread-safe implementation. Generally when you run into the question of thread-safety, most people have no idea what you are talking about. The problem with writing truly thread-safe code that can work in any scenario is that you will sacrifice performance. If you tailor your thread-safe mechanisms to your particular problem, you can generally maintain a higher level of performance.
The reason we need thread-safety is not that we are specifically spawning threads. In IIS, worker processes are inherently multi-threaded. Therefore, using static objects in a web-farm generally requires some level of thread safety, otherwise you will get dreaded "An item with the same key has already been added" errors. This is because between the time you check to see if a key already exists in your dictionary and the time you insert it, some other thread may have inserted this key. This is especially the case in object factories, because you have multiple threads attempting to access/create objects and subsequently cache them.
This is the code I've written. This has not been tested yet, so please use it carefully. I will keep this post updated with any changes and test results. I also have not made this class utilize lock escalation, which ReaderWriterLockSlim supports. As always, please perform your own testing.
ThreadSafeDictionary.zip
Filed under: Dictionary, Threading, C#