MemoryCache nasıl temizlenir?


100

MemoryCache sınıfını kullanarak bir önbellek oluşturdum. İçine bazı öğeler ekliyorum ama önbelleği yeniden yüklemem gerektiğinde önce onu temizlemek istiyorum. Bunu yapmanın en hızlı yolu nedir? Tüm öğeleri gözden geçirip birer birer çıkarmalı mıyım yoksa daha iyi bir yol var mı?


1
.NET çekirdeği için bu yanıtı kontrol edin .
Makla

Yanıtlar:


61

Dispose mevcut MemoryCache ve yeni bir MemoryCache nesnesi oluşturun.


3
Başlangıçta MemoryCache.Default'u kullandım ve Dispose'un bana biraz üzülmesine neden oldum. Yine de, Dispose bulabildiğim en iyi çözüm oldu. Teşekkürler.
LaustN

11
@LaustN MemoryCache.Default'un neden olduğu "keder" hakkında ayrıntılı bilgi verebilir misiniz? Şu anda MemoryCache.Default kullanıyorum ... MSDN'nin MemoryCache belgeleri, bertaraf etmenin ve yeniden oluşturmanın tavsiye edilip edilmediğini merak etmeme neden oluyor: "Gerekmedikçe MemoryCache örnekleri oluşturmayın. İstemci ve Web uygulamalarında önbellek örnekleri oluşturursanız, MemoryCache örnekleri uygulama yaşam döngüsünün başlarında oluşturulabilir. " Bu .Default için geçerli mi? Dispose kullanmanın yanlış olduğunu söylemiyorum, dürüst olmak gerekirse tüm bunlarla ilgili açıklama istiyorum.
ElonU Webdev

8
Mevcut önbelleğe alınmış öğelere ekli herhangi bir Dispose şeyi çağırdığından bahsetmeye değer olduğunu düşündüm CacheEntryRemovedCallback.
Mike Guthrie

8
@ElonU: Aşağıdaki Stack Overflow yanıtı, varsayılan örneğin elden çıkarılmasıyla karşılaşabileceğiniz bazı sıkıntıları açıklar: stackoverflow.com/a/8043556/216440 . Alıntı yapmak gerekirse: "Önbelleğin durumu, önbelleğin atıldığını gösterecek şekilde ayarlanmıştır. Önbellek girişlerini ekleyen, kaldıran veya alan yöntemler gibi önbelleğin durumunu değiştiren genel önbelleğe alma yöntemlerini çağırma girişimleri, beklenmedik Örneğin, önbellek atıldıktan sonra Set yöntemini çağırırsanız, işlem yok hatası oluşur. "
Simon Tewsi

56

Numaralandırma ile ilgili problem

MemoryCache.GetEnumerator () bölümünde Açıklamalar uyarır: ", bir kaynak yoğun ve engelleme işlemini bir MemoryCache örneği için bir sýralayýcý olan alma Bu nedenle, Numaralayıcı üretim uygulamalarında kullanılmamalıdır."

GetEnumerator () uygulamasının sözde kodunda açıklanan nedeni aşağıda açıklanmıştır:

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

Uygulama, önbelleği birden çok Dictionary nesnesine böldüğünden, bir numaralandırıcıyı geri vermek için her şeyi tek bir koleksiyonda bir araya getirmelidir. GetEnumerator'a yapılan her çağrı, yukarıda ayrıntıları verilen tam kopyalama işlemini yürütür. Yeni oluşturulan Sözlük, orijinal dahili anahtar ve değer nesnelerine referanslar içerir, böylece gerçek önbelleğe alınmış veri değerleriniz çoğaltılmaz.

Belgelerdeki uyarı doğrudur. GetEnumerator () 'dan kaçının - LINQ sorgularını kullanan yukarıdaki tüm yanıtlar dahil.

Daha iyi ve daha esnek bir çözüm

İşte, mevcut değişiklik izleme altyapısını temel alan, önbelleği temizlemenin etkili bir yolu. Ayrıca, önbelleğin tamamını veya yalnızca adlandırılmış bir alt kümeyi temizleme esnekliği sağlar ve yukarıda tartışılan sorunların hiçbirine sahip değildir.

// 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
Eksik Bölge işlevselliği için bir uygulama gibi görünüyor.
Jowen

Çok hoş. Zincirlenmiş bellek önbellek monitörlerini ve kılavuzlarını kullanarak bir şeyler uygulamaya çalışıyordum, ancak işlevselliği sıkılaştırmaya çalıştığımda biraz çirkinleşmeye başlamıştı.
Chao

7
Bu kalıbı genel kullanım için tavsiye etmem. 1. Yavaştır, uygulama hatası yoktur, ancak atma yöntemi son derece yavaştır. 2. Son kullanma tarihi geçmiş öğeleri önbellekten çıkarırsanız, İzleme değiştir hala çağrılır. 3. Makinem tüm CPU'yu yutuyordu ve performans testlerini çalıştırırken önbellekteki 30.000 öğeyi temizlemek gerçekten çok uzun sürdü. 5+ dakika bekledikten sonra birkaç kez testleri bitirdim.
Aaron M

1
@PascalMathys Maalesef bundan daha iyi bir çözüm yok. Dezavantajlarına rağmen, numaralandırmayı kullanmaktan daha iyi bir çözüm olduğu için kullanmaya son verdim.
Aaron M

9
@AaronM Bu çözüm, önbelleği atıp yenisini başlatmaktan daha mı iyi?
RobSiklos

35

Gönderen http://connect.microsoft.com/VisualStudio/feedback/details/723620/memorycache-class-needs-a-clear-method

Çözüm şudur:

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

33
Gönderen belgeler : a MemoryCache örneği için bir sýralayýcý alınıyor bir kaynak yoğun ve engelleme işlemidir. Bu nedenle, üretim uygulamalarında numaralandırıcı kullanılmamalıdır.
TrueWill

3
@emberdude Bu, bir numaralandırıcıyı almakla tamamen aynıdır - uygulamasının ne olduğunu düşünüyorsunuz Select()?
RobSiklos

1
Kişisel olarak, bunu her birim testi için bellek önbelleğini temizlemek için birim testi [TestInitialize] işlevimde kullanıyorum. Aksi takdirde, 2 işlev arasında performansı karşılaştırmaya çalışırken, önbellek birim testlerde kalır ve istenmeyen sonuçlar verir.
Jacob Morrison

6
@JacobMorrison tartışmasız, birim testleri bir "üretim uygulaması" değil :)
Mels

1
@Mels tartışmasız, birim testleri "üretim uygulaması" ile aynı standartlarda yazılmalıdır! :)
Etherman


10

Performans bir sorun değilse, bu güzel tek satırlık hile yapacak:

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


3

Bunun gibi bir şey de yapabilirsiniz:


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

3

Bununla karşılaştım ve buna dayanarak biraz daha etkili, paralel açık bir yöntem yazdı:

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

1
Daha hızlı (veya daha yavaş) olup olmadığını görmek için test ettiniz mi?
Paul George

1

Sadece önbelleği temizlemekle ilgileniyordum ve bunu c # GlobalCachingProvider'ı kullanırken bir seçenek olarak buldum

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

0

magritte cevabının biraz geliştirilmiş versiyonu.

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 önbelleğini atabilir ve ardından MemoryCache.Default'u yeniden oluşturması için özel alanı tekli olarak null olarak yeniden ayarlayabilirsiniz.

       var field = typeof(MemoryCache).GetField("s_defaultCache",
            BindingFlags.Static |
            BindingFlags.NonPublic);
        field.SetValue(null, null);
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.