Двойная проверка блокировки на словаре "ContainsKey" - PullRequest
14 голосов
/ 16 мая 2011

Моя команда в настоящее время обсуждает эту проблему.

Код, о котором идет речь, похож на

if (!myDictionary.ContainsKey(key))
{
    lock (_SyncObject)
    {
        if (!myDictionary.ContainsKey(key))
        {
            myDictionary.Add(key,value);
        }
    }
}

В некоторых постах, которые я видел, говорится, что это может бытьбольшой НЕТ НЕТ (при использовании TryGetValue).Тем не менее, члены нашей команды говорят, что все в порядке, так как «ContainsKey» не выполняет итерацию по сбору ключей, а проверяет, содержится ли ключ в хеш-коде в O (1).Поэтому они утверждают, что здесь нет никакой опасности.

Я хотел бы получить ваше честное мнение по этому вопросу.

Ответы [ 5 ]

26 голосов
/ 16 мая 2011

Не делай этого.Это небезопасно.

Вы можете звонить ContainsKey из одного потока, в то время как другой поток вызывает Add.Это просто не поддерживается Dictionary<TKey, TValue>.Если Add нужно перераспределить сегменты и т. Д., Я могу представить, что может получить очень странные результаты или исключение.Это возможно было написано так, что вы не видите никаких неприятных эффектов, но я бы не хотел на это полагаться.

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

Если вы находитесь на .NET 4, ConcurrentDictionary, вероятно, путь вперед.В противном случае просто заблокируйте каждый доступ.

6 голосов
/ 16 мая 2011

Если вы находитесь в многопоточной среде, вы можете предпочесть использование ConcurrentDictionary. Я писал об этом пару месяцев назад, вам может пригодиться статья: http://colinmackay.co.uk/blog/2011/03/24/parallelisation-in-net-4-0-the-concurrent-dictionary/

5 голосов
/ 16 мая 2011

Этот код неверен.Тип Dictionary<TKey, TValue> не поддерживает одновременные операции чтения и записи.Даже если ваш Add метод вызывается внутри блокировки, ContainsKey - нет.Следовательно, он легко допускает нарушение правила одновременного чтения / записи и приведет к повреждению в вашем случае

1 голос
/ 16 мая 2011

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

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

0 голосов
/ 07 ноября 2017

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

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

    public static Schema GetSchema(Type type)
    {
        if (_schemaLookup.TryGetValue(type, out Schema schema))
            return schema;

        lock (_syncRoot) {
            if (_schemaLookup.TryGetValue(type, out schema))
                return schema;

            var newLookup = new Dictionary<Type, Schema>(_schemaLookup);

            foreach (var t in type.Assembly.GetTypes()) {
                var newSchema = new Schema(t);
                newLookup.Add(t, newSchema);
            }

            _schemaLookup = newLookup;

            return _schemaLookup[type];
        }
    }

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

Вы можете применять аналогичные принципы и в других ситуациях.

...