วิธีล้าง MemoryCache


100

ฉันได้สร้างแคชโดยใช้คลาส MemoryCache ฉันเพิ่มบางรายการเข้าไป แต่เมื่อฉันต้องการโหลดแคชใหม่ฉันต้องการล้างมันก่อน อะไรคือวิธีที่เร็วที่สุดในการดำเนินการนี้ ฉันควรวนซ้ำรายการทั้งหมดและลบทีละรายการหรือมีวิธีที่ดีกว่านี้


1
สำหรับหลัก .NET ตรวจสอบนี้คำตอบ
Makla

คำตอบ:


62

Dispose MemoryCache ที่มีอยู่และสร้างวัตถุ MemoryCache ใหม่


3
ตอนแรกฉันใช้ MemoryCache ค่าเริ่มต้นทำให้ Dispose ทำให้ฉันรู้สึกเศร้า อย่างไรก็ตามการกำจัดทิ้งกลายเป็นทางออกที่ดีที่สุดที่ฉันสามารถหาได้ ขอบคุณ.
LaustN

11
@LaustN คุณสามารถอธิบายอย่างละเอียดเกี่ยวกับ "ความเศร้าโศก" ที่เกิดจาก MemoryCache.Default? ฉันกำลังใช้ MemoryCache อยู่ค่าเริ่มต้น ... เอกสาร MemoryCache ของ MSDN ทำให้ฉันสงสัยว่าแนะนำให้ทิ้งและสร้างใหม่หรือไม่: "อย่าสร้างอินสแตนซ์ MemoryCache เว้นแต่จะจำเป็นหากคุณสร้างอินสแตนซ์แคชในไคลเอนต์และเว็บแอปพลิเคชันอินสแตนซ์ MemoryCache ควร ถูกสร้างขึ้นในช่วงต้นของวงจรชีวิตของแอปพลิเคชัน " สิ่งนี้ใช้กับ. Default หรือไม่ ฉันไม่ได้บอกว่าการใช้ Dispose นั้นผิดฉันแค่ต้องการคำชี้แจงทั้งหมดนี้
ElonU Webdev

8
คิดว่ามันคุ้มค่าที่Dispose จะกล่าวถึงที่เรียกสิ่งที่CacheEntryRemovedCallbackแนบมากับรายการแคชปัจจุบัน
Mike Guthrie

8
@ElonU: ต่อไปนี้คำตอบที่กองมากเกินจะอธิบายบางส่วนของความเศร้าโศกที่คุณอาจพบการกำจัดของอินสแตนซ์เริ่มต้น: stackoverflow.com/a/8043556/216440 ในการอ้างอิง: "สถานะของแคชถูกกำหนดให้ระบุว่าแคชถูกกำจัดความพยายามใด ๆ ที่จะเรียกใช้วิธีการแคชสาธารณะที่เปลี่ยนสถานะของแคชเช่นวิธีการที่เพิ่มลบหรือดึงข้อมูลรายการแคชอาจทำให้เกิดสิ่งที่ไม่คาดคิด ลักษณะการทำงานตัวอย่างเช่นหากคุณเรียกใช้เมธอด Set หลังจากที่แคชถูกกำจัดจะเกิดข้อผิดพลาด no-op "
Simon Tewsi

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
ดูเหมือนเป็นการใช้งานสำหรับฟังก์ชันภูมิภาคที่ขาดหายไป
Jowen

ดีมาก. ฉันพยายามใช้งานบางอย่างโดยใช้จอภาพและคู่มือหน่วยความจำแบบล่ามโซ่ แต่มันเริ่มน่าเกลียดไปหน่อยเมื่อฉันพยายามกระชับฟังก์ชันการทำงาน
เจ้า

7
ฉันไม่แนะนำให้ใช้รูปแบบนี้สำหรับการใช้งานทั่วไป 1. มันช้าไม่มีข้อผิดพลาดในการใช้งาน แต่วิธีการกำจัดนั้นช้ามาก 2. หากการขับไล่รายการของคุณออกจากแคชด้วยการหมดอายุ Change monitor จะยังคงถูกเรียก 3. เครื่องของฉันกำลังกลืนซีพียูทั้งหมดและใช้เวลานานมากในการล้างรายการ 30k ออกจากแคชเมื่อฉันทำการทดสอบประสิทธิภาพ ไม่กี่ครั้งหลังจากรอ 5+ นาทีฉันก็ฆ่าการทดสอบ
Aaron M

1
@PascalMathys น่าเสียดายไม่มีทางออกที่ดีไปกว่านี้ ฉันใช้มันได้แม้จะมีข้อเสีย แต่ก็ยังคงเป็นทางออกที่ดีกว่าการใช้การแจงนับ
Aaron M

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);
}

34
จากเอกสารประกอบ : การดึงตัวแจงนับสำหรับอินสแตนซ์ MemoryCache เป็นการดำเนินการที่ใช้ทรัพยากรมากและปิดกั้น ดังนั้นจึงไม่ควรใช้ตัวแจงนับในงานการผลิต
TrueWill

3
@emberdude มันเหมือนกับการดึงตัวแจงทุกประการ - คุณคิดอย่างไรกับการนำไปใช้Select()?
RobSiklos

1
โดยส่วนตัวฉันใช้สิ่งนี้ในฟังก์ชันทดสอบหน่วย [TestInitialize] เพื่อล้างแคชหน่วยความจำสำหรับการทดสอบแต่ละหน่วย มิฉะนั้นแคชจะยังคงอยู่ในการทดสอบหน่วยโดยให้ผลลัพธ์ที่ไม่ได้ตั้งใจเมื่อพยายามเปรียบเทียบประสิทธิภาพระหว่าง 2 ฟังก์ชัน
Jacob Morrison

6
@JacobMorrison เนื้อหาการทดสอบหน่วยไม่ใช่ "แอปพลิเคชันการผลิต" :)
Mels

1
@Mels เนื้อหาการทดสอบหน่วยควรเขียนให้เป็นมาตรฐานเดียวกันกับ "แอปพลิเคชันการผลิต"! :)
Etherman

21
var cacheItems = cache.ToList();

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

3
สิ่งนี้มีความเสี่ยงเช่นเดียวกับคำตอบของ @ Tony; โปรดดูความคิดเห็นของฉันที่ด้านล่าง
TrueWill

@TrueWill คือใครหรือเป็น @Tony?
Alex Angas

2
@AlexAngas - เขาอาจจะเปลี่ยนชื่อเป็น magritte ดูstackoverflow.com/questions/4183270/…
TrueWill


7

ดูเหมือนว่าจะมีวิธีการตัดแต่ง

เพื่อล้างเนื้อหาทั้งหมดที่คุณเพิ่งทำ

cache.Trim(100)

แก้ไข: หลังจากขุดเพิ่มเติมดูเหมือนว่าการดู Trim จะไม่คุ้มค่ากับเวลาของคุณ

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
คุณทดสอบเพื่อดูว่าเร็วขึ้น (หรือช้ากว่า)?
Paul George

1

ฉันสนใจแค่การล้างแคชและพบว่านี่เป็นตัวเลือกเมื่อใช้ c # GlobalCachingProvider

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

0

คำตอบ magritte รุ่นปรับปรุงเล็กน้อย

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 แคชดีฟอลต์แล้วตั้งค่าไพรเวตฟิลด์ singleton ใหม่เป็น null เพื่อสร้าง MemoryCache ใหม่

       var field = typeof(MemoryCache).GetField("s_defaultCache",
            BindingFlags.Static |
            BindingFlags.NonPublic);
        field.SetValue(null, null);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.