Як очистити MemoryCache?


100

Я створив кеш, використовуючи клас MemoryCache. Я додаю до нього деякі елементи, але коли мені потрібно перезавантажити кеш, я хочу спершу очистити його. Який найшвидший спосіб зробити це? Чи повинен я переглядати всі елементи та видаляти їх по одному або є кращий спосіб?


1
Для ядра .NET перевірте цю відповідь.
Макла

Відповіді:


61

Dispose існуючий MemoryCache та створити новий об'єкт MemoryCache.


3
Я спочатку використовував MemoryCache.Default, викликаючи Dispose, щоб дати мені трохи горя. І все-таки, розпорядження виявилося найкращим рішенням, яке я міг знайти. Дякую.
LaustN

11
@LaustN чи можете ви детальніше розповісти про "горе", спричинене MemoryCache.Default? В даний час я використовую MemoryCache.Default ... Документація MemoryCache MSDN змушує мене замислитись, чи не рекомендується розміщувати та відтворювати: "Не створюйте екземпляри MemoryCache, якщо це не потрібно. Якщо ви створюєте екземпляри кеша в клієнтських та веб-програмах, екземпляри MemoryCache повинні створюється на початку життєвого циклу програми. " Це стосується .Default? Я не кажу, що використання Dispose неправильно, я чесно просто шукаю роз'яснення щодо всього цього.
ElonU Webdev

8
Думав , що це варто згадати , що Dispose робить викликати будь- CacheEntryRemovedCallbackприкладені до поточних пунктам кешованих.
Майк Гутрі

8
@ElonU: Наступна відповідь про переповнення стека пояснює деяку тугу, яку ви можете зіткнутися з утилізацією екземпляра за замовчуванням: stackoverflow.com/a/8043556/216440 . Цитую: "Стан кешу встановлено, щоб вказати, що кеш розміщений. Будь-яка спроба викликати методи кешування публічного кешування, які змінюють стан кеша, такі як методи, які додають, видаляють або отримують записи кеша, можуть спричинити несподіване поведінка. Наприклад, якщо ви викликаєте метод Set після того, як кеш буде розміщений, виникає помилка без операції. "
Саймон Тевсі

56

Проблема з перерахуванням

Розділ зауважень MemoryCache.GetEnumerator () попереджає: "Отримання перечислювача для екземпляра MemoryCache - це ресурсомістка та блокувальна операція. Тому перечислювач не повинен використовуватися у виробничих додатках."

Ось чому , пояснено в псевдокоді реалізації GetEnumerator ():

Create a new Dictionary object (let's call it AllCache)
For Each per-processor segment in the cache (one Dictionary object per processor)
{
    Lock the segment/Dictionary (using lock construct)
    Iterate through the segment/Dictionary and add each name/value pair one-by-one
       to the AllCache Dictionary (using references to the original MemoryCacheKey
       and MemoryCacheEntry objects)
}
Create and return an enumerator on the AllCache Dictionary

Оскільки реалізація розбиває кеш на декілька об'єктів словника, вона повинна об'єднати все в єдину колекцію, щоб повернути перелік. Кожен виклик GetEnumerator виконує повний процес копіювання, детально описаний вище. Щойно створений Словник містить посилання на оригінальний внутрішній ключ та об'єкти значень, тому фактичні значення кешованих даних не дублюються.

Попередження в документації правильне. Уникайте GetEnumerator () - включаючи всі відповіді вище, що використовують запити LINQ.

Краще і гнучкіше рішення

Ось ефективний спосіб очищення кешу, який просто спирається на існуючу інфраструктуру моніторингу змін. Він також забезпечує гнучкість очистити або весь кеш, або лише названий підмножина, і не має жодної з проблем, обговорених вище.

// By Thomas F. Abraham (http://www.tfabraham.com)
namespace CacheTest
{
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.Caching;

    public class SignaledChangeEventArgs : EventArgs
    {
        public string Name { get; private set; }
        public SignaledChangeEventArgs(string name = null) { this.Name = name; }
    }

    /// <summary>
    /// Cache change monitor that allows an app to fire a change notification
    /// to all associated cache items.
    /// </summary>
    public class SignaledChangeMonitor : ChangeMonitor
    {
        // Shared across all SignaledChangeMonitors in the AppDomain
        private static event EventHandler<SignaledChangeEventArgs> Signaled;

        private string _name;
        private string _uniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);

        public override string UniqueId
        {
            get { return _uniqueId; }
        }

        public SignaledChangeMonitor(string name = null)
        {
            _name = name;
            // Register instance with the shared event
            SignaledChangeMonitor.Signaled += OnSignalRaised;
            base.InitializationComplete();
        }

        public static void Signal(string name = null)
        {
            if (Signaled != null)
            {
                // Raise shared event to notify all subscribers
                Signaled(null, new SignaledChangeEventArgs(name));
            }
        }

        protected override void Dispose(bool disposing)
        {
            SignaledChangeMonitor.Signaled -= OnSignalRaised;
        }

        private void OnSignalRaised(object sender, SignaledChangeEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(e.Name) || string.Compare(e.Name, _name, true) == 0)
            {
                Debug.WriteLine(
                    _uniqueId + " notifying cache of change.", "SignaledChangeMonitor");
                // Cache objects are obligated to remove entry upon change notification.
                base.OnChanged(null);
            }
        }
    }

    public static class CacheTester
    {
        public static void TestCache()
        {
            MemoryCache cache = MemoryCache.Default;

            // Add data to cache
            for (int idx = 0; idx < 50; idx++)
            {
                cache.Add("Key" + idx.ToString(), "Value" + idx.ToString(), GetPolicy(idx));
            }

            // Flush cached items associated with "NamedData" change monitors
            SignaledChangeMonitor.Signal("NamedData");

            // Flush all cached items
            SignaledChangeMonitor.Signal();
        }

        private static CacheItemPolicy GetPolicy(int idx)
        {
            string name = (idx % 2 == 0) ? null : "NamedData";

            CacheItemPolicy cip = new CacheItemPolicy();
            cip.AbsoluteExpiration = System.DateTimeOffset.UtcNow.AddHours(1);
            cip.ChangeMonitors.Add(new SignaledChangeMonitor(name));
            return cip;
        }
    }
}

8
Схоже, реалізація для відсутнього функціоналу регіону.
Джовен

Дуже хороша. Я намагався реалізувати щось, використовуючи приковані монітори та керуючі пам’яті кеш-пам’яті, але це було трохи негарно, коли я намагався посилити функціонал.
Чао

7
Я б не рекомендував цю схему для загального використання. 1. Її повільність, не винна реалізація, але спосіб утилізації надзвичайно повільний. 2. Якщо ваші виселення елементів із кешу закінчуються, програма монітора зміниться, як і раніше. 3. Моя машина проковтнула весь процесор і зайняла дуже тривалий час, щоб очистити 30 к. Елементів із кешу, коли я виконував тести на ефективність. Кілька разів після очікування 5+ хвилин я просто вбив тести.
Аарон М

1
@PascalMathys На жаль, немає кращого рішення, ніж це. Я, нарешті, використовував його, незважаючи на недоліки, як його все-таки краще рішення, ніж використання перерахунку.
Аарон М

9
@AaronM Це рішення все-таки краще, ніж просто видалити кеш і створити новий?
RobSiklos

35

Від http://connect.microsoft.com/VisualStudio/feedback/details/723620/memorycache-class-needs-a-clear-method

Вирішення:

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

33
З документації : Отримання обчислювача для екземпляра MemoryCache - це ресурсомістка та блокувальна операція. Тому нумератор не повинен використовуватися у виробничих додатках.
TrueWill

3
@emberdude Це точно так само, як отримати нумератор - чим ви займаєтеся реалізацією Select()?
RobSiklos

1
Особисто я використовую це в своїй тестовій одиниці [TestInitialize], щоб очистити кеш пам'яті для кожного тесту одиниці. Інакше кеш зберігається через тести одиниць, що дають ненавмисні результати при спробі порівняння продуктивності між двома функціями.
Джейкоб Моррісон

6
@JacobMorrison Можливо, одиничні тести не є "виробничим додатком" :)
Mels

1
@Mels, певно, одиничні тести повинні бути написані за тими ж стандартами, що і "виробнича заявка"! :)
Етерман

21
var cacheItems = cache.ToList();

foreach (KeyValuePair<String, Object> a in cacheItems)
{
    cache.Remove(a.Key);
}

3
Це має той самий ризик, що і у відповіді @ Тоні; будь ласка, дивіться мій коментар під цим.
TrueWill

@TrueWill Хто є або був @Tony?
Олексій Ангас

2
@AlexAngas - Він, можливо, змінив ім'я на магріт. Дивіться також stackoverflow.com/questions/4183270 / ...
TrueWill

10

Якщо продуктивність не є проблемою, то цей чудовий однокласник зробить свою справу:

cache.ToList().ForEach(a => cache.Remove(a.Key));

7

Здається, що існує метод Trim .

Отже, щоб очистити весь вміст, який ви просто зробили

cache.Trim(100)

РЕДАКТУЙТЕ: після копання ще трохи здається, що заглядати в обробку не варто свого часу

https://connect.microsoft.com/VisualStudio/feedback/details/831755/memorycache-trim-method-doesnt-evict-100-of-the-items

Як очистити систему System.Runtime.Caching.MemoryCache


3

Ви також можете зробити щось подібне:


Dim _Qry = (From n In CacheObject.AsParallel()
           Select n).ToList()
For Each i In _Qry
    CacheObject.Remove(i.Key)
Next

3

Перебіг цього і на основі цього написав дещо ефективніший, паралельно зрозумілий метод:

    public void ClearAll()
    {
        var allKeys = _cache.Select(o => o.Key);
        Parallel.ForEach(allKeys, key => _cache.Remove(key));
    }

1
Ви перевірили, чи швидше (чи повільніше)?
Пол Джордж

1

Мене зацікавило лише очищення кешу, і я знайшов це як варіант, коли використовував c # GlobalCachingProvider

                var cache = GlobalCachingProvider.Instance.GetAllItems();
                if (dbOperation.SuccessLoadingAllCacheToDB(cache))
                {
                    cache.Clear();
                }

0

трохи вдосконалена версія відповіді магрітта.

var cacheKeys = MemoryCache.Default.Where(kvp.Value is MyType).Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
{
    MemoryCache.Default.Remove(cacheKey);
}

0

Ви можете розпоряджатись кешем MemoryCache.Default, а потім знову встановлювати приватне поле singleton на нуль, щоб він відтворив MemoryCache.Default.

       var field = typeof(MemoryCache).GetField("s_defaultCache",
            BindingFlags.Static |
            BindingFlags.NonPublic);
        field.SetValue(null, null);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.