ReaderWriterLockSlim против.Монитор
-
03-07-2019 - |
Вопрос
у меня есть IDictionary<TKey,TValue>
реализация, которая внутри содержит другие Dictionary<TKey, TValue>
и распределяет эти вставки по HashCode ключа по отдельным подсловарям.При наличии 16 подсловарей количество коллизий на 4-ядерной машине довольно низкое.
Для параллельных вставок я заблокировал метод Add с помощью ReaderWriterLockSlim
, блокируя только отдельный подсловарь:
public void Add(TKey key, TValue value)
{
int poolIndex = GetPoolIndex(key);
this.locks[poolIndex].EnterWriteLock();
try
{
this.pools[poolIndex].Add(key, value);
}
finally
{
this.locks[poolIndex].ExitWriteLock();
}
}
При вставке элементов с четырьмя потоками я получил только около 32% загрузки процессора и плохую производительность.Поэтому я заменил ReaderWriterLockSlim на монитор (т. е. lock
ключевое слово).Загрузка ЦП теперь достигла почти 100%, а производительность выросла более чем вдвое.
Мой вопрос:Почему увеличилась загрузка ЦП?Количество столкновений не должно было измениться.Что заставляет ReaderWriterLock.EnterWriteLock ждать столько раз?
Решение
Для загрузки только для записи Monitor дешевле, чем ReaderWriterLockSlim, однако, если вы моделируете нагрузку чтения + записи, где чтение намного превышает запись, тогда ReaderWriterLockSlim должен превзойти Monitor.
Другие советы
Я не гуру, но я предполагаю, что RWLS больше ориентирован на тяжелые конфликты (например, сотни потоков), тогда как Monitor
более приспособлен к этим разовым проблемам синхронизации.
Лично я использую TimerLock
класс, который использует Monitor.TryEnter
с параметром таймаута.
Как узнать, что стало причиной плохой производительности?Вы не можете об этом догадаться, единственный способ — провести какое-то профилирование.
Как вы справляетесь с блокировкой родительской коллекции или она постоянна?
Может быть, вам нужно добавить отладочный вывод и посмотреть, что произойдет на самом деле?