Я начал работать с некоторым кодом .NET 3.5 и обнаружил, что для кэша используется следующий метод расширения:
public static TValue GetOrAdd<TKey, TValue>(this Dictionary<TKey, TValue> @this, TKey key,Func<TKey,TValue> factory,bool useLocking)
{
TValue value;
if(!@this.TryGetValue(key,out value))
{
if (useLocking)
{
lock ((@this as ICollection).SyncRoot)
{
if (!@this.TryGetValue(key, out value))
{
@this[key] = value = factory(key);
}
}
}
else
{
@this[key] = value = factory(key);
}
}
return value;
}
Кэш, о котором идет речь, имеет строковые ключи и использует useLocking = true. К этому методу всегда обращаются (нет никаких помех TryGetValue
). Также нет проблем с использованием свойства SyncRoot
, так как словарь является частным и нигде не используется. Двойная блокировка опасна, потому что словарь не поддерживает чтение во время записи. Хотя технически об этой проблеме еще не сообщалось, поскольку продукт не поставлялся, я чувствую, что такой подход приведет к гоночным условиям.
Переключите Dictionary<,>
на Hashtable
. Мы потеряем безопасность типов, но мы сможем поддерживать модель параллелизма, которая нам нужна (1 писатель, несколько читателей).
Удалите внешнее TryGetValue. Таким образом, каждое чтение требует блокировки. Это потенциально плохо сказывается на производительности, но приобретает неоспоримое замок должен быть довольно дешево.
Оба довольно дерьмовые.
У кого-нибудь есть лучшее предложение? Если бы это был код .NET 4, я бы просто переключил его на ConcurrentDictionary
, но у меня нет такой опции.