Dave Mateer
Dave Mateer

Reputation: 17946

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

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.

Upvotes: 5

Views: 339

Answers (1)

Chris Shain
Chris Shain

Reputation: 51329

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.

Upvotes: 3

Related Questions