Когда ConcurrentDictionary TryRemove вернет false

79

Будет ли он возвращать false только в том случае, если словарь не содержит значения для данного ключа, или он также вернет false из-за условий гонки потоков, например, другой поток добавляет / обновляет что-то?

Вопрос в коде:

ConcurrentDictionary<int, string> cd = new ConcurrentDictionary<int, string>();

// This might fail if another thread is adding with key value of 1.
cd.TryAdd(1, "one"); 

// Will this ever fail if no other thread ever removes with the key value of 1?
cd.TryRemove(1); 

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

Мартин Ингвар Кофоед Йенсен
источник

Ответы:

85

Хотя Митч прав в том, что a ConcurrentDictionaryне уязвим для условий гонки, я думаю, что ответ на вопрос, который вы задаете, - да, если ключ присутствует, TryRemoveбудет работать и вернется true.

В опубликованном вами коде нет TryRemoveвозврата, falseпоскольку cdэто локальная переменная, к которой больше нигде нет доступа. Но если в каком-то другом коде была указана ссылка на этот ConcurrentDictionaryобъект и он удалял ключи в отдельном потоке, тогда возможно, что он TryRemoveмог вернуться false, даже здесь - но только потому, что ключ уже был удален , а не потому, что какое-то другое действие выполняется над словарь и ключ как-то там "застряли".

Дэн Тао
источник
5

ConcurrentDictionary не страдает от условий гонки. Вот почему вы его используете.

Возвращаемое значение

истина, если объект был успешно удален; в противном случае - ложь.

Митч Уит
источник
2

Еще одно замечание:

// This might fail if another thread is adding with key value of 1.
cd.TryAdd(1, "one"); 

Этот комментарий неверен и, возможно, страдает от того же неправильного представления о том, что значит «попробовать». Дело не в одновременной попытке добавления, а в том, было ли уже добавлено значение с ключом 1.

Считайте эталон Dictionary<TKey,TValue>. Эквивалентный код будет:

if (!d.Contains(1))
    d.Add(1, "one");

Это требует двух операций. Невозможно спроектировать такой API, чтобы он был потокобезопасным, поскольку cdмогло бы иметь значение с ключом, 1добавленным между вызовом Containsи Add, что затем привело бы к Addвыбрасыванию.

У параллельных коллекций есть API-интерфейсы, которые логически объединяют эти пары «тест и выполнение» в отдельные атомарные операции за одним API.

Дрю Ноукс
источник