Вчера я обнаружил, мы имели многопоточности вопрос с помощью простого объекта кэширования мы используем:Какой фиксирующий примитив использовать?
If Dictionary.Contains(lsKey.ToLower) Then 'if rate cached, then return value
lvResult = Dictionary.Item(lsKey.ToLower)
Else 'else retrieve from database, store, and return value
lvResult = GetRateFromDB(voADO,
veRateType,
vdEffDate)
Dictionary.Add(lsKey.ToLower, lvResult)
End If
Мы обнаружили эту проблему на нашем сайте asp.net. Сообщение об ошибке прочитало что-то вроде «вы пытаетесь добавить значение к хеш-таблице, которая уже существует. Как вы можете сказать из вышеприведенного кода, потенциал, безусловно, завершается, чтобы это произошло. Я был немного знаком с waithandles и думал, что они разрешат проблему . Таким образом, я объявил мой WaitHandle на уровне класса:
private Shared _waitHandle as new AutoResetEvent(True)
Затем в специальном разделе кода с проблемой:
_waitHandle.Wait()
If Dictionary.Contains(lsKey.ToLower) Then 'if rate cached, then return value
lvResult = Dictionary.Item(lsKey.ToLower)
Else 'else retrieve from database, store, and return value
lvResult = GetRateFromDB(voADO,
veRateType,
vdEffDate)
Dictionary.Add(lsKey.ToLower, lvResult)
End If
_waitHandle.Set()
по какой-то причине следующий код выше был всегда заблокирован Даже очень. первый поток, доступ к коду. Я некоторое время играл с вещами и даже пытался настроить waithandle на сигнализируется в конструкторе, но я никогда не мог заставить его работать.
я в конечном итоге, используя следующие вместо которой работает отлично:
SyncLock loLock
If Dictionary.Contains(lsKey.ToLower) Then 'if rate cached, then return value
lvResult = Dictionary.Item(lsKey.ToLower)
Else 'else retrieve from database, store, and return value
lvResult = GetRateFromDB(voADO,
veRateType,
vdEffDate)
Dictionary.Add(lsKey.ToLower, lvResult)
End If
End SyncLock
Поэтому у меня есть два вопроса:
- Почему не работу WaitHandle решение?
- Является ли SynLock правильным/оптимизированным типом блокировки для использования в этом случае?
Переход на ['ConcurrentDictionary'] (http://msdn.microsoft.com/en-us/library/dd287191.aspx) будет гораздо более надежным решением, если вы используете .NET 4 или более позднюю версию , –
Если вы переключитесь на 'ConcurrentDictionary', вам нужно будет использовать метод AddOrUpdate(). Если вы просто сохранили вышеуказанный шаблон ('if .Contains(), else .Add()'), у вас все еще есть условие гонки, так как поток может добавить тот же ключ после вызова .Contains(). –
Хотя 'AutoResetEvent', вероятно, не является подходящим методом блокировки, объяснения, данные двумя ответами до сих пор, похоже, не правильно объясняют, почему метод' AutoResetEvent', который вы пробовали, не работал. Одна из возможностей заключается в том, что исключение было выбрано во время 'GetRateFromDB', предотвращая вызов' _waitHandle.Set() ', и поэтому все потоки в конечном итоге блокируются на' Wait() '. Помещение 'Set()' в блок 'finally', скорее всего, решит это (хотя опять же, есть более эффективные методы синхронизации, чем' AutoResetEvent'). – Iridium