ConcurrentDictionary <TKey, TValue> vs Dictionary <TKey, TValue>
Как MSDN говорит
ConcurrentDictionary<TKey, TValue>
Класс Представляет потокобезопасную коллекцию пар ключ-значение, к которым одновременно можно обращаться несколькими потоками.
Но, как я знаю, классы System.Collections.Concurrent
предназначены для PLINQ.
У меня есть Dictionary<Key,Value>
, который держит он-лайн клиентов на сервере, и я делаю его потокобезопасным, блокируя объект, когда у меня есть к нему доступ.
Могу ли я безопасно заменить Dictionary<TKey,TValue>
на ConcurrentDictionary<TKey,TValue>
в моем случае? увеличится ли производительность после замены?
Здесь в части 5 Джозеф Альбахари упомянул, что он предназначен для параллельного программирования
- Совпадающие коллекции настраиваются для параллельного программирования. Обычные коллекции превосходят их во всех, но очень параллельных сценариях.
- Нитебезопасная коллекция не гарантирует, что использующий ее код будет потокобезопасным.
- Если вы перечисляете параллельную коллекцию, а другой поток ее модифицирует, исключение не генерируется. Вместо этого вы получаете смесь старого и нового контента.
- Отсутствует параллельная версия списка.
- Сопутствующие классы стека, очереди и пакета реализуются внутри со связанными списками. Это делает их менее эффективными с точки зрения памяти, чем неконкурентные классы Stack и Queue, но лучше для одновременного доступа, поскольку связанные списки способствуют реализации без блокировки или с низкой блокировкой. (Это связано с тем, что вставка node в связанный список требует обновления только нескольких ссылок, в то время как вставка элемента в структуру, подобную List, может потребовать перемещения тысяч существующих элементов.)
Ответы
Ответ 1
Не зная больше о том, что вы делаете в замке, тогда это невозможно сказать.
Например, если весь доступ к вашему словарю выглядит следующим образом:
lock(lockObject)
{
foo = dict[key];
}
... // elsewhere
lock(lockObject)
{
dict[key] = foo;
}
Тогда вы будете нормально его отключать (хотя вы, вероятно, не увидите никакой разницы в производительности, поэтому, если он не сломался, не исправить). Однако, если вы делаете что-либо в блоке блокировки, где вы взаимодействуете со словарем, тогда вам нужно будет убедиться, что словарь предоставляет единственную функцию, которая может выполнить то, что вы делаете в блоке блокировки, иначе вы В итоге вы получите код, функционально отличающийся от того, что у вас было раньше. Самое главное помнить, что словарь гарантирует, что одновременные вызовы словаря исполняются серийно; он не может обрабатывать случаи, когда в вашем коде есть одно действие, которое несколько раз взаимодействует со словарем. Такие случаи, когда не учитываются ConcurrentDictionary
, требуют вашего собственного элемента управления concurrency.
К счастью, ConcurrentDictionary
предоставляет некоторые вспомогательные функции для более общих многошаговых операций, таких как AddOrUpdate
или GetOrAdd
, но они не могут охватить все обстоятельства. Если вы обнаружите, что вам нужно работать, чтобы обучить вашу логику этим функциям, лучше справиться с вашим собственным concurrency.
Ответ 2
Это не так просто, как замена Dictionary
на ConcurrentDictionary
, вам нужно будет адаптировать ваш код, поскольку у этих классов есть новые методы, которые ведут себя по-другому, чтобы гарантировать безопасность потоков.
Например, вместо вызова Add
или Remove
у вас есть TryAdd
и TryRemove
. Важно, чтобы вы использовали эти методы, которые ведут себя атомарно, как если бы вы делали два вызова, в которых вторая зависит от результата первого, у вас все еще будут условия гонки и требуется lock
.
Ответ 3
Вы можете заменить Dictionary<TKey, TValue>
на ConcurrentDictionary<TKey, TValue>
.
Эффект на производительность может быть не таким, каким вы хотите (если есть много блокировки/синхронизации, производительность может пострадать... но по крайней мере ваша коллекция не подвержена потоку).
Ответ 4
Пока я не уверен в проблемах с заменой, но если у вас есть где-то, где вам нужно получить доступ к нескольким элементам в словаре в том же "сеансе блокировки", вам нужно будет изменить свой код.
Это может обеспечить улучшенную производительность, если Microsoft предоставила отдельные блокировки для чтения и записи, поскольку операции чтения не должны блокировать другие операции чтения.
Ответ 5
Да, вы можете спокойно заменить, однако словарь, предназначенный для plinq, может содержать дополнительный код для добавления функциональных возможностей, которые вы не можете использовать. Но накладные расходы производительности будут незначительно очень маленькими.