Как очистить System.Runtime.Caching.MemoryCache - PullRequest
35 голосов
/ 08 ноября 2011

Я использую System.Runtime.Caching.MemoryCache для хранения предметов, срок действия которых никогда не истекает. Однако иногда мне нужна возможность очистить весь кэш. Как мне это сделать?

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

Я пытался использовать .Trim(100), но это совсем не работает.

Я пытался получить список всех ключей через Linq, но затем я вернулся к тому, с чего начал, потому что выселение предметов по одному может легко привести к условиям гонки.

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

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

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

Итак, как мне полностью очистить кеш?

Ответы [ 7 ]

31 голосов
/ 24 декабря 2012

Сначала я боролся с этим. MemoryCache.Default.Trim (100) не работает (как обсуждалось). Trim - это лучшая попытка, поэтому, если в кеше 100 элементов и вы вызываете Trim (100), он удалит наименее используемые.

Trim возвращает количество удаленных элементов, и большинство людей ожидает, что все элементы будут удалены.

Этот код удаляет все элементы из MemoryCache для меня в моих тестах xUnit с MemoryCache.Default. MemoryCache.Default является регионом по умолчанию.

foreach (var element in MemoryCache.Default)
{
    MemoryCache.Default.Remove(element.Key);
}
16 голосов
/ 08 ноября 2011

Вы не должны вызывать dispose на элементе MemoryCache по умолчанию, если вы хотите больше его использовать:

Состояние кэша установлено, чтобы указать, что кэш удален.Любая попытка вызвать общедоступные методы кэширования, которые изменяют состояние кэша, например методы, которые добавляют, удаляют или извлекают записи кэша, могут вызвать непредвиденное поведение.Например, если вы вызываете метод Set после удаления кэша, возникает ошибка no-op.Если вы попытаетесь извлечь элементы из кэша, метод Get всегда будет возвращать Nothing.http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.dispose.aspx

Что касается Trim, он должен работать:

Свойство Trim сначала удаляет записи, которые превысили абсолютный или скользящий срок действия.Любые обратные вызовы, которые зарегистрированы для элементов, которые были удалены, будут переданы по причине истечения срока действия.

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

Но два других пользователя сообщили, что он не работает на той же странице, поэтому я полагаю, что вы застряли с Remove () http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.trim.aspx

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

Но если вам нужно освободитьпамяти из экземпляра по умолчанию, вам придется очистить ее вручную или окончательно уничтожить с помощью dispose (что делает ее непригодной для использования).

Исходя из вашего вопроса, вы можете сделать свой собственный внушающий одиночный класс, возвращающий Memorycache, который вы можете внутренне уничтожитьпо желанию .. Быть природой кеша: -)

8 голосов
/ 28 апреля 2015

Вот то, что я сделал для чего-то, над чем я работал ...

public void Flush()
{
    List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList();
    foreach (string cacheKey in cacheKeys)
    {
        MemoryCache.Default.Remove(cacheKey);
    }
}
2 голосов
/ 22 марта 2019

Я знаю, что это старый вопрос, но лучший вариант, с которым я столкнулся, это

Удалите существующий MemoryCache и создайте новый объект MemoryCache. https://stackoverflow.com/a/4183319/880642

Ответ на самом деле не предоставляет код, позволяющий сделать это потокобезопасным способом. Но это может быть достигнуто с помощью Interlocked.Exchange

var oldCache = Interlocked.Exchange(ref _existingCache, new MemoryCache("newCacheName"));
oldCache.Dispose();

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

2 голосов
/ 08 ноября 2011

Подробности в ответе @ stefan подробно описывают принцип;вот как я это сделаю.

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

Чтобы избежать этой синхронизации, сделайте это в своем классе адаптера (который оборачивает MemoryCache):

public void clearCache() {
  var oldCache = TheCache;
  TheCache = new MemoryCache("NewCacheName", ...);
  oldCache.Dispose();
  GC.Collect();
}

Таким образом, TheCache всегда находится в неразмещенном состоянии, и синхронизация не требуется.

0 голосов
/ 03 апреля 2014

Проверьте этот пост, и, в частности, ответ, который Томас Ф. Авраам опубликовал.У него есть решение, которое позволяет очистить весь кэш или именованное подмножество.

Ключевым моментом здесь является:

// Cache objects are obligated to remove entry upon change notification.
base.OnChanged(null);

Я сам реализовал это, и, кажется, все работает отлично.

0 голосов
/ 03 января 2013

Я тоже столкнулся с этой проблемой..Dispose () сделал нечто совершенно иное, чем я ожидал.

Вместо этого я добавил статическое поле в свой класс контроллера.Я не использовал кеш по умолчанию, чтобы обойти это поведение, но создал приватный (если вы хотите так его называть).Моя реализация выглядела примерно так:

public class MyController : Controller
{

    static MemoryCache s_cache = new MemoryCache("myCache");

    public ActionResult Index()
    {

        if (conditionThatInvalidatesCache)
        {
            s_cache = new MemoryCache("myCache");
        }

        String s = s_cache["key"] as String;

        if (s == null)
        {
            //do work
            //add to s_cache["key"]
        }

        //do whatever next
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...