Правильный способ блокировки словарного объекта
В моем коде у меня есть объект статического словаря
private static IDictionary< ConnKey, DbConnection > ConnectionList = new Dictionary< ConnKey, DbConnection >( );
который выбрасывает эту ошибку
System.IndexOutOfRangeException: Index was outside the bounds of the array.
at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
Я искал и обнаружил, что это происходит, потому что несколько потоков пытаются получить доступ к словарю, но у меня есть lock
в словаре
lock( ConnectionList ) {
ConnectionList.Add( key, res );
}
Затем я искал больше и обнаружил, что блокировка словаря не предотвращает все операции над ним, поэтому я должен использовать объект lock
on SyncRoot
для этого, чтобы добиться того, что хочу
lock( ((IDictionary)ConnectionList).SyncRoot) {
Но потом я искал, что использование SyncRoot
не является хорошей практикой
При дальнейшем поиске я обнаружил, что для этого существует ConcurrentDictionary
- Так может кто-нибудь, пожалуйста, предложите мне, который является лучшим способом блокировки словаря.
- Если я использую
ConcurrentDictionary
, мне все равно нужно использовать lock
на нем или он сам будет обрабатывать все.
- Если мне нужно использовать lock на
ConcurrentDictionary
, я должен использовать lock
на нем напрямую или снова, мне нужно заблокировать объект SyncRoot
для него
Спасибо заранее!
Ответы
Ответ 1
С Dictionary<,>
вам нужно заблокировать чтение и запись. Таким образом, оба
lock( ConnectionList ) {
ConnectionList.Add( key, res );
}
и
lock( ConnectionList ) {
res = ConnectionList[ key ];
}
и
lock( ConnectionList ) {
int cnt = ConnectionList.Count;
}
и
lock( ConnectionList ) {
ConnectionList.Clear();
}
и
lock( ConnectionList ) {
foreach ( var kv in ConnectionList ) {
// Do things
}
}
и т.д.: -)
С ConcurrentDictionary<,>
вам не нужна блокировка, но обратите внимание, что синтаксис немного отличается от синтаксиса Dictionary<,>
Ответ 2
- Так может кто-нибудь, пожалуйста, предложите мне, который является лучшим способом блокировки словаря.
Вы можете использовать его SyncRoot
или создать закрытый объект, который вы блокируете при доступе к объекту словаря, например.
private static object _sybcRoot = new object();
public static void Add( string key, string res)
lock( _sybcRoot ) {
ConnectionList.Add( key, res );
}
}
- Если я использую ConcurrentDictionary, мне все равно нужно использовать блокировку на нем или он сам будет обрабатывать все.
Нет, нет необходимости блокировать при использовании какой-либо коллекции Concurrent*
. Это поточно-безопасный по дизайну, но этот синтаксис несколько отличается. Concurrent*
коллекции используют запретный подход, что лучше в ситуациях, когда у вас нет большого количества потоков, конкурирующих за доступ (оптимистичный concurrency)
- Если мне нужно использовать lock в ConcurrentDictionary, я должен использовать блокировку непосредственно или снова, мне нужно заблокировать объект SyncRoot для него.
Вы должны использовать один и тот же объект блокировки для защиты доступа к одному и тому же ресурсу. В противном случае потоки могут "думать", что ресурс свободен, тогда как на самом деле он используется другим потоком, который просто блокирует его на другом корне.
Ответ 3
Так может ли кто-нибудь предложить мне, который является лучшим способом блокировки словаря?
если вы хотите продолжить использование классического Dictionary<,>
AFAK, вам нужно искать интерфейс ICollection
, реализованный в словаре, и использовать свойство ICollection.SyncRoot
который по определению
MSDN Gets an object that can be used to synchronize access to the ICollection.
Чтобы достичь этого, вы можете сделать что-то вроде этого
Если я использую ConcurrentDictionary, мне все равно нужно использовать блокировку на нем или он будет обрабатывать все сам по себе.
От MSDN
ConcurrentDictionary предназначен для многопоточных сценариев. Вам не нужно использовать блокировки в вашем коде для добавления или удаления элементов из коллекции. Тем не менее, всегда возможно, чтобы один поток извлекал значение, а другой поток немедленно обновлял коллекцию, предоставляя тому же ключу новое значение.
Если мне нужно использовать lock на ConcurrentDictionary, я должен использовать блокировку на нем напрямую или снова, мне нужно заблокировать объект SyncRoot для него
Да, вы должны использовать lock
на SyncRoot
, если хотите выполнить выполнение Atomic-методов при использовании методов GetOrAdd
или AddOrUpdate