Reputation: 17946
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
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