Правильное использование параллельного словаря

84

Правильно ли я считаю, что это правильное использование параллельного словаря

private ConcurrentDictionary<int,long> myDic = new ConcurrentDictionary<int,long>();

//Main thread at program startup

for(int i = 0; i < 4; i++)
{
  myDic.Add(i, 0);
}

//Seperate threads use this to update a value

myDic[InputID] = newLongValue;

У меня нет блокировок и т. Д., И я просто обновляю значение в словаре, хотя несколько потоков могут пытаться сделать то же самое.

Джон
источник
2
Это зависит - newLongValueзависит от предыдущего значения myDic[InputID]?
Damien_The_Unbeliever
3
вам следует избегать прямого доступа по ключу из- myDic[InputID]за состояния гонки. Вы должны попробоватьGetOrAdd
Оливье Альбертини
3
@OlivierAlbertini, я не думаю, что myDic[InputID]вызывает какие-либо проблемы, когда он используется в качестве lvalue. GetOrAddне является правильной заменой, поскольку добавляет, только если значение не существует. Вместо этого мы можем использовать AddOrUpdateдля добавления / обновления того же значения в словаре.
Джатин Сангхви 09

Ответы:

75

Это зависит от того, что вы подразумеваете под потокобезопасностью.

Из MSDN - Как: добавлять и удалять элементы из ConcurrentDictionary :

ConcurrentDictionary<TKey, TValue>разработан для многопоточных сценариев. Вам не нужно использовать блокировки в коде для добавления или удаления элементов из коллекции. Однако всегда есть возможность для одного потока получить значение, а для другого потока немедленно обновить коллекцию, присвоив тому же ключу новое значение.

Таким образом, можно получить противоречивое представление о значении элемента в словаре.

Одед
источник
2
Это интересный момент! Вы бы по-прежнему использовали блокировку в этом сценарии?
Джон
@Jon - Это зависит от вашего приложения, и согласны ли вы. Но я бы сказал, что если вам нужны согласованные представления элементов, вам нужно будет заключать каждое чтение и обновление элемента в блокировку.
Oded
12
Я думаю, что это не то, о чем говорится в документе. Несогласованность связана с тем, что содержит представление, если представление - это просто значение, тогда оно полностью согласовано. Пока вы получаете значение ключа, значение ключа в словаре может измениться. Это так же непоследовательно, как и значение DateTime.Now.
Георгий Маврицакис
4

Лучший способ узнать это - проверить документацию MSDN.

Для ConcurrentDictionary страница http://msdn.microsoft.com/en-us/library/dd287191.aspx

В разделе, посвященном безопасности потоков, говорится: «Все открытые и защищенные члены ConcurrentDictionary (Of TKey, TValue) являются потокобезопасными и могут использоваться одновременно из нескольких потоков».

Так что с точки зрения параллелизма все в порядке.

Эрдем
источник
2

Да ты прав.

Это и возможность перечислить словарь в одном потоке при изменении его в другом потоке - единственные средства существования для этого класса.

Янв
источник
10
Я бы хотел добавить, что вот полезная информация о том, как и когда использовать ConcurrentDictionary.
alex.b
1

В зависимости от обстоятельств, в моем случае я предпочитаю использовать этот метод.

ConcurrentDictionary<TKey, TValue>.AddOrUpdate Method (TKey, Func<TKey, TValue>, Func<TKey, TValue, TValue>);

См. Библиотеку MSDN об использовании метода .

Пример использования:

results.AddOrUpdate(
  Id,
  id => new DbResult() {
     Id = id,
     Value = row.Value,
     Rank = 1
  },
  (id, v) =>
  {
     v.Rank++;
     return v;
  });
Онур
источник
2
К вашему сведению: «Когда вы предоставляете метод фабрики значений (методам GetOrAdd и AddOrUpdate), он действительно может работать, а его результат впоследствии отбрасывается (потому что какой-то другой поток выиграл гонку)». Подробнее здесь: arbel.net/2013/02/03/…
keremispirli
Да, вы правы, как указано в разделе примечаний: «Если вы вызываете AddOrUpdate одновременно в разных потоках, addValueFactory может вызываться несколько раз, но его пара ключ / значение может не добавляться в словарь для каждого вызова». Поэтому вам нужно быть уверенным, что вы не создаете несколько постоянных объектов.
Онур
И если вам нужно обновить содержимое, а не полностью изменять сохраненный объект, например, чтобы изменить свойство ранее добавленного объекта, этот метод полезен, в противном случае вам нужно использовать блокировки или другие методы синхронизации.
Онур
1

Просто примечание: не оправдывает использование объекта ConcurrentDicitonary с линейным циклом, что делает его малоиспользуемым. Лучшая альтернатива - следовать рекомендациям документации Microsoft, упомянутым Одедом с использованием параллелизма, в соответствии с приведенным ниже примером:

Parallel.For(0, 4, i => 
{
   myDic.TryAdd(i, 0);
});
Антонио Леонардо
источник