How do I efficiently support both item- and collection-level locking?

.net, c#, multithreading

Solution

I'd prefix my answer with the assumption that you have benchmarked this multithreaded behavior vs marshalling all work to a single thread. If not, please do- you may find it is much faster to avoid the lock contention entirely.

What you are looking for is a reader writer lock, probably ReaderWriterLockSlim in .net. To update a single item, take a read lock, then `lock()` the item, do the update, and release the `lock()` and then the read lock. To do an "all items" update, an add, or a remove, take a write lock, which will be exclusive.

Problem

I have an application has a collection of domain objects that need to be updated in real-time. Several threads can take action that modify the items in this collection and must do so safely. The current approach is rather simplistic, taking essentially a global lock before making any changes. More or less something like the following: ``` private readonly object lockObject = new object(); private Dictionary<int, Widget> items; private void UpdateAllWidgets() { lock (this.lockObject) { // Update all the widgets. No widgets should be permitted to be // updated while this is running. } } private void UpdateWidget(int widgetId) { lock (this.lockObject) { // Update the widget with id = widgetId. I want to me able to // update other widgets at the same time, however. } } ``` I am now running into problems with performance because the lock is too coarse. I would like to be able to take item-level locks while a single item is being updated (allowing other items to be updated at the same time), but still be able to take a collection-level lock when necessary. So the behavior would look like the following: ``` Thread 1: UpdateWidget(1); Thread 2: UpdateWidget(2); // This can run before UpdateWidget(1) // completes. Thread 1: UpdateWidget(1); Thread 2: UpdateAllWidgets(); // This has to wait for UpdateWidget(1) Thread 3: UpdateWidget(2); // This has to wait for UpdateAllWidgets() Thread 4: UpdateWidget(3); // This has to wait for UpdateAllWidgets(), but // not UpdateWidget(2) ``` Any ideas on a lock structure that would support this? The example above is necessary a simplified version, but we have already ruled out ConcurrentDictionary has being insufficient. The actions taken in, for example, UpdateWidget, are more than just simple in-place updates. They could involve database updates that impact that item, etc.

Original source