Как очистить System.Runtime.Caching.MemoryCache

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

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

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

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

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

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

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

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

6 ответов

Решение

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

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

Про трим, он должен работать:

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

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

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

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

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

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

Сначала я боролся с этим. 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);
}

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

Удалите существующий MemoryCache и создайте новый объект MemoryCache. /questions/5226246/kak-ochistit-memorycache/5226257#5226257

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

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

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

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

public void Flush()
{
    List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList();
    foreach (string cacheKey in cacheKeys)
    {
        MemoryCache.Default.Remove(cacheKey);
    }
}

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

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

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

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

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

Я столкнулся с этой проблемой тоже. .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
    }
}

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

Главное здесь:

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

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

Другие вопросы по тегам