2009-12-22 11 views
99

Я не мог найти достаточно информации о типах ConcurrentDictionary, поэтому я подумал, что я бы спросил об этом здесь..NET - блокировка словаря против ConcurrentDictionary

В настоящее время я использую Dictionary, чтобы держать всех пользователей, к которым постоянно обращаются несколько потоков (из пула потоков, поэтому нет точного количества потоков), и у него есть синхронизированный доступ.

Недавно я узнал, что в .NET 4.0 существует набор потокобезопасных коллекций, и это кажется очень приятным. Мне было интересно, каков будет вариант «более эффективный и простой в управлении», поскольку у меня есть опция между нормальным Dictionary с синхронизированным доступом или ConcurrentDictionary, который уже является потокобезопасным.

Reference to .NET 4.0's ConcurrentDictionary

+2

Вы можете увидеть [этот код проекта статьи по теме] (Http: //www.codeproject.com/Статьи/548406/Словарь-плюс-Блокировка-против-ConcurrentDictionar) – nawfal

ответ

124

Потокобезопасная коллекция по сравнению с не-потолочной коллекцией можно посмотреть по-другому.

Рассмотрите возможность покупки в магазине без присмотра, кроме как при оформлении заказа. У вас много проблем, если люди не действуют ответственно. Например, предположим, что клиент берет банку из пирамиды, пока клерк в настоящее время строит пирамиду, весь ад сломается. Или, что, если два клиента достигнут одного и того же предмета одновременно, кто победит? Будет ли бой? Это нетекающая коллекция. Существует множество способов избежать проблем, но все они требуют какой-то блокировки или, скорее, явного доступа в той или иной форме.

С другой стороны, рассмотрите магазин с клерком на столе, и вы можете только ходить по нему через магазин. Вы попадаете в очередь и спрашиваете его о предмете, он возвращает его вам, и вы выходите из линии. Если вам нужно несколько предметов, вы можете подобрать столько предметов на каждом кругообороте, как вы помните, но вам нужно быть осторожным, чтобы не дотрагиваться до клерка, это вызовет гнев других клиентов в очереди за вами.

Теперь рассмотрим это. В магазине с одним клерком, что, если вы доберетесь до передней части линии и спросите клерка «У вас есть туалетная бумага», и он говорит «Да», а затем вы идете «Хорошо, я», я вернусь к вам, когда я знаю, сколько мне нужно », то к тому времени, когда вы вернетесь на фронт линии, магазин, конечно же, может быть распродан. Этот сценарий не препятствует сборке потоковых сетей.

Коллекция потокобезопасности гарантирует, что ее внутренние структуры данных действительны во все времена, даже если они доступны из нескольких потоков.

В комплект поставки, не входящей в потолок, не предоставляются такие гарантии. Например, если вы добавляете что-то в двоичное дерево в одном потоке, в то время как другой поток занят перебалансировкой дерева, нет никакой гарантии, что элемент будет добавлен, или даже что дерево все еще действует после этого, оно может быть повреждено без надежды.

Коллекция поточно не означает, однако, гарантировать, что последовательные операции на резьбе всю работу на том же «снимок» своей внутренней структуры данных, что означает, что если у вас есть такой код:

if (tree.Count > 0) 
    Debug.WriteLine(tree.First().ToString()); 

вы можете получить исключение NullReferenceException, потому что между tree.Count и tree.First() другой поток очистил оставшиеся узлы в дереве, что означает, что First() вернет null.

Для этого сценария вам нужно либо посмотреть, есть ли у сборника безопасный способ получить то, что вы хотите, возможно, вам нужно переписать код выше, или вам может потребоваться блокировка.

+7

Каждый раз, когда я читаю эту статью, хотя все это правда, мы почему-то идем по неверному пути. –

+18

Основная проблема в приложении с поддержкой потоков - изменяемые структуры данных. Если вы можете этого избежать, вы сбережете себе кучу неприятностей. –

+0

Очевидно, словари могут иметь некоторые действительно неприятные побочные эффекты: http://stackoverflow.com/questions/14838032/asp-net-hang-generic-dictionary-concurrency-issues-causes-gc-deadlock – jocull

5

В основном вы хотите пойти с новым ConcurrentDictionary. Прямо из коробки вы должны написать меньше кода, чтобы создавать потокобезопасные программы.

+0

Есть ли какие-либо проблемы, если я буду продолжать Concurrent Dictionery? – kbvishnu

53

Вы по-прежнему должны быть очень осторожны при использовании потокобезопасных коллекций, потому что потокобезопасность не означает, что вы можете игнорировать все проблемы с потоками. Когда коллекция рекламирует себя как потокобезопасную, обычно это означает, что она остается в согласованном состоянии, даже когда несколько потоков читают и записывают одновременно. Но это не означает, что один поток будет видеть «логическую» последовательность результатов, если он вызывает несколько методов.

Например, если вы сначала проверяете, существует ли ключ, а затем получают значение, соответствующее ключу, этот ключ больше не может существовать даже с версией ConcurrentDictionary (поскольку другой поток мог удалить ключ). Вам все равно нужно использовать блокировку в этом случае (или лучше: объединить два вызова с помощью TryGetValue).

Так что используйте их, но не думайте, что он дает вам бесплатный пропуск, чтобы игнорировать все проблемы параллелизма. Вы все еще должны быть осторожны.

+4

Итак, в основном вы говорите, что если вы не используете объект правильно, он не будет работать? – ChaosPandion

+0

Почему? Как-то коллекция лишается изменения. –

+1

В основном вам нужно использовать TryAdd вместо обычного Contains -> Add. – ChaosPandion

12

Я думаю, что метод ConcurrentDictionary.GetOrAdd - это именно то, что нужно большинству многопоточных сценариев.

+1

Я бы тоже использовал TryGet, иначе вы тратите усилия на инициализацию второго параметра GetOrAdd каждый раз, когда ключ уже существует. –

+5

* GetOrAdd * имеет перегрузку * GetOrAdd (TKey, Func ) *, поэтому второй параметр может быть инициализирован только в том случае, если ключ не существует в словаре. Кроме того * TryGetValue * используется для извлечения данных, а не для изменения. Последующие вызовы для каждого из этих методов могут привести к тому, что другой поток может добавить или удалить ключ между двумя вызовами. – sgnsajgon

+0

Это должен быть заметный ответ на вопрос, хотя сообщение Лассе отлично. – Spivonious

13

Вы видели Reactive Extensions для .Net 3.5sp1. По словам Джона Скита, они предоставили пакет параллельных расширений и параллельных структур данных для .Net3.5 sp1.

Существует набор образцов для .Net 4 Beta 2, в котором довольно подробно описаны способы использования параллельных расширений.

Я провела последнюю неделю тестирования ConcurrentDictionary, используя 32 потока для выполнения операций ввода-вывода. Кажется, он работает как рекламируемый, что указывает на то, что в него было внесено огромное количество тестов.

Редактировать: .NET 4 ConcurrentDictionary и образцы.

Microsoft выпустила документ под названием «Шаблоны программирования Paralell». Его поистине стоит скачать, поскольку он описывает в действительно хороших деталях правильные шаблоны для использования .Net 4 параллельных расширений и анти-шаблонов, которых следует избегать. Here it is.

27

Внутренний ConcurrentDictionary использует отдельный замок для каждого хэш-ведра. До тех пор, пока вы используете только Add/TryGetValue и подобные методы, которые работают с одиночными записями, словарь будет работать как почти блокируемая структура данных с соответствующей слабой производительностью. OTOH перечисляющие методы (включая свойство Count) блокируют все ковши сразу и, следовательно, хуже, чем синхронизированный Словарь, по производительности.

Я бы сказал, просто используйте ConcurrentDictionary.

2

Мы использовали ConcurrentDictionary для кэшированной коллекции, которая повторно заполняется каждые 1 час, а затем считывается несколькими клиентскими потоками, аналогично to the solution для вопроса Is this example thread safe?.

Мы обнаружили, что изменение его на ReadOnlyDictionary улучшило общую производительность.

+2

Я ценю, что знаю причину downvote? –

+0

ReadOnlyDictionary - это всего лишь обертка вокруг другого IDictionary. Что вы используете под капотом? – Lu55