.NET - блокировка словаря и ConcurrentDictionary
Я не смог найти достаточно информации о типах ConcurrentDictionary
, поэтому решил спросить об этом здесь.
Dictionary
для хранения всех пользователей, к которым постоянно обращаются несколько потоков (из пула потоков, поэтому нет точного количества потоков), и он имеет синхронизированный доступ.
Недавно я узнал, что в .NET 4.0 есть набор потокобезопасных коллекций, и это, кажется, очень приятно. Мне было интересно, какой вариант будет "более эффективным и простым в управлении", поскольку я есть выбор между обычным Dictionary
с синхронизированным доступом или иметь ConcurrentDictionary
, который уже потокобезопасен.
8 ответов:
На потокобезопасную коллекцию и не-потокобезопасную коллекцию можно смотреть по-другому.
Рассмотрим магазин, в котором нет клерка, кроме как на кассе. У вас будет куча проблем, если люди не будут действовать ответственно. Например, скажем, клиент берет банку из пирамиды-банка, в то время как клерк в настоящее время строит пирамиду, весь ад вырвется на свободу. Или, если два клиента тянутся к одному и тому же товару одновременно, кто выигрывает? Будет ли бой? Это non-threadsafe-коллекция. Существует множество способов избежать проблем, но все они требуют какой-то блокировки, или, скорее, явного доступа тем или иным способом. С другой стороны, рассмотрим магазин с продавцом за столом, и вы можете делать покупки только через него. Вы становитесь в очередь и просите у него товар, он возвращает его вам, и вы выходите из очереди. Если вам нужно несколько предметов, вы можете забрать только столько предметов в каждой поездке, сколько вы можете вспомнить, но вы должны быть осторожны, чтобы избегайте избиения клерка, это разозлит других клиентов в очереди позади вас. Теперь рассмотрим это. В магазине с одним продавцом, что если Вы дойдете до конца очереди и спросите продавца "есть ли у вас туалетная бумага", и он скажет "Да", а затем вы скажете "Хорошо, я вернусь к вам, когда буду знать, сколько мне нужно", то к тому времени, когда вы вернетесь в начало очереди, магазин, конечно, может быть продан. Этот сценарий не предотвращается threadsafe коллекция. Коллекция threadsafe гарантирует, что ее внутренние структуры данных действительны в любое время, даже если доступ осуществляется из нескольких потоков., не ориентирована на многопотоковое исполнение коллекции нет таких гарантий. Например, если вы добавляете что-то в двоичное дерево в одном потоке, в то время как другой поток занят перебалансировкой дерева, нет никакой гарантии, что элемент будет добавлен, или даже что дерево будет все еще действительным после этого, оно может быть безнадежно повреждено.
A threadsafe collection, однако, не гарантирует, что все последовательные операции над потоком работают на одном и том же" снимке " его внутренней структуры данных, что означает, что если у вас есть код, подобный этому:
if (tree.Count > 0) Debug.WriteLine(tree.First().ToString());
Вы можете получить исключение NullReferenceException, потому что между
tree.Count
иtree.First()
другой поток очистил оставшиеся узлы в дереве, что означает, чтоFirst()
вернетnull
.Для этого сценария вам либо нужно посмотреть, есть ли у рассматриваемой коллекции безопасный способ получить то, что вы хотите, возможно, вам нужно переписать код выше, или вам может потребоваться блокировка.
Вам все равно нужно быть очень осторожным при использовании потокобезопасных коллекций, потому что потокобезопасность не означает, что вы можете игнорировать все проблемы с потоками. Когда коллекция объявляет себя потокобезопасной, это обычно означает, что она остается в согласованном состоянии, даже когда несколько потоков одновременно читают и пишут. Но это не означает, что один поток будет видеть "логическую" последовательность результатов, если он вызывает несколько методов.
Например, если вы сначала проверяете, существует ли ключ и затем позже получить значение, соответствующее ключу, что ключ может больше не существовать даже с ConcurrentDictionary версии (потому что другой поток может удалить ключ). В этом случае вам все равно нужно использовать блокировку (или лучше: объединить два вызова с помощью TryGetValue).
Так что используйте их, но не думайте, что это дает вам право игнорировать все проблемы параллелизма. Тебе все равно нужно быть осторожным.
Внутренне ConcurrentDictionary использует отдельную блокировку для каждого хэш-ведра. Пока вы используете только Add/TryGetValue и подобные методы, которые работают с отдельными записями, словарь будет работать как почти свободная от блокировки структура данных с соответствующим преимуществом производительности. OTOH методы перечисления (включая свойство Count) блокируют все сегменты сразу и поэтому хуже, чем синхронизированный словарь, с точки зрения производительности.
Я бы сказал, просто используйте ConcurrentDictionary.
Вы видели реактивные расширения для .Net 3.5sp1. По словам Джона Скита, они вернули пакет параллельных расширений и параллельных структур данных для .Net3. 5 sp1.
Существует набор примеров для .Net 4 Beta 2, который довольно подробно описывает, как использовать их параллельные расширения.
Я только что провел последнюю неделю, тестируя ConcurrentDictionary, используя 32 потока для выполнения ввода-вывода. укажите, что в него было вложено огромное количество испытаний.
Редактировать: .Сеть 4 ConcurrentDictionary и узоры.
Компания Microsoft выпустила PDF-файлов под названием модели программирования, параллельной. Его действительно стоит загрузить, поскольку он описал в очень хороших деталях правильные шаблоны для использования для параллельных расширений .Net 4 и анти-шаблонов, чтобы избежать. Вот оно.
Я думаю, что ConcurrentDictionary.Метод GetOrAdd-это именно то, что нужно большинству многопоточных сценариев.
В основном вы хотите пойти с новым ConcurrentDictionary. Прямо из коробки вы должны написать меньше кода, чтобы сделать потокобезопасные программы.
Мы использовали ConcurrentDictionary для кэшированной коллекции, которая повторно заполняется каждые 1 час и затем читается несколькими клиентскими потоками, аналогично решению для является ли этот пример потокобезопасным? вопрос.
Мы обнаружили, что изменение его на ReadOnlyDictionary улучшило общую производительность.
Есть хороший курс о параллельных коллекциях в c# under the hood: параллельные коллекции Pluralsight
Там вы найдете всю интересную информацию.