Безопасное использование "HttpContext.Current.Cache"

Я использую Cache в методе веб-службы, подобном этому:

var pblDataList = (List<blabla>)HttpContext.Current.Cache.Get("pblDataList");

if (pblDataList == null)
{
    var PBLData = dc.ExecuteQuery<blabla>(@"SELECT blabla");

    pblDataList = PBLData.ToList();

    HttpContext.Current.Cache.Add("pblDataList", pblDataList, null,
        DateTime.Now.Add(new TimeSpan(0, 0, 15)),
        Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
}

Но интересно, этот код безопасен? Метод веб-службы вызывается несколькими пользователями. И еще один запросчик может попытаться извлечь данные и добавить к Cache одновременно, пока кеш пуст.

Запрос занимает от 5 до 8 секунд. Внедрил бы инструкцию блокировки вокруг этого кода, чтобы предотвратить любые возможные конфликты? (Я знаю, что несколько запросов могут выполняться одновременно, но я хочу быть уверенным, что за один раз запускается только один запрос.)

Ответы

Ответ 1

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

Если у вас есть шанс получить доступ к кешу из фонового потока, вместо этого используйте HttpRuntime.Cache.

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

Однако в большинстве случаев вам действительно нужно сначала профилировать и посмотреть, действительно ли это проблема. Большинство веб-приложений/служб не относятся к этому аспекту кэширования, потому что они не имеют состояния, и не имеет значения, перезаписывается ли кеш.

Ответ 2

Вы правы. Операции получения и добавления не рассматриваются как атомная транзакция. Если вам нужно предотвратить многократное выполнение запроса, вам необходимо использовать блокировку.

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

Ответ 3

Я считаю, что Add должен быть потокобезопасным - то есть он не будет ошибочным, если Add дважды вызывается с тем же ключом, но, очевидно, запрос может выполняться дважды.

Другой вопрос, однако, в том, что это потоки данных. Нет никакой гарантии, что каждый List<blabla> изолирован - это зависит от поставщика кеша. Поставщик кэша в памяти хранит объекты напрямую, поэтому существует риск коллизий, если какой-либо поток обрабатывает данные (добавляет/удаляет/заменяет элементы в списке или меняет свойства одного из элементов). Однако с помощью поставщика сериализации вы должны быть в порядке. Конечно, это требует, чтобы blabla был сериализуемым...