Обновление: Это приемлемо, если этот метод не является потокобезопасным, но мне интересно узнать, как я сделает его потокобезопасным. Кроме того, я не хочу блокировать один объект для всех значений ключа
, если я могу этого избежать.
Исходный вопрос: Предположим, я хочу написать функцию более высокого порядка, которая принимает ключ и функцию и проверяет, был ли объект кэширован с данным ключом. Если есть, возвращается кешированное значение. В противном случае данная функция запускается, а результат кэшируется и возвращается.
Вот упрощенная версия моего кода:
public static T CheckCache(string key, Func fn, DateTime expires)
{
object cache = HttpContext.Current.Cache.Get(key);
//clearly not thread safe, two threads could both evaluate the below condition as true
//what can I lock on since the value of "key" may not be known at compile time?
if (cache == null)
{
T result = fn();
HttpContext.Current.Cache.Insert(key, result, null, expires, Cache.NoSlidingExpiration);
return result;
}
else
return (T)cache;
}
Кроме того, предположим, что я не знаю всех возможных значений ключа
во время компиляции .
Как я могу сделать этот поток безопасным? Я знаю, что здесь мне нужно ввести блокировку, чтобы 1+ потоки не оценили мое условие как истинное, но я не знаю, что заблокировать. Многие из прочитанных мной примеров блокировки (например, статья Джона Скита ) рекомендуют использовать «фиктивную» частную переменную, которая используется только для блокировки. В данном случае это невозможно, потому что во время компиляции ключи неизвестны. Я знаю, что могу тривиально сделать этот поток безопасным, используя одну и ту же блокировку для каждого ключа
, но это может быть расточительным.
Теперь мой главный вопрос:
Можно ли заблокировать на клавише
? Поможет ли здесь интернирование строк?
После прочтения интернирования строк в .NET 2.0 наизнанку я понимаю, что могу явно вызвать String.Intern ()
для получения сопоставления 1 к 1 от значения строки до экземпляра строки. Подходит ли это для блокировки? Давайте изменим приведенный выше код на:
public static T CheckCache(string key, Func fn, DateTime expires)
{
//check for the scenario where two strings with the same value are stored at different memory locations
key = String.Intern(key);
lock (key) //is this object suitable for locking?
{
object cache = HttpContext.Current.Cache.Get(key);
if (cache == null)
{
T result = fn();
HttpContext.Current.Cache.Insert(key, result, null, expires, Cache.NoSlidingExpiration);
return result;
}
else
return (T)cache;
}
}
Является ли приведенная выше реализация потокобезопасной?