Làm thế nào để xóa MemoryCache?


100

Tôi đã tạo một bộ nhớ cache bằng cách sử dụng lớp MemoryCache. Tôi thêm một số mục vào nó nhưng khi tôi cần tải lại bộ nhớ cache, tôi muốn xóa nó trước. Cách nhanh nhất để làm điều này là gì? Tôi có nên lặp lại tất cả các mục và xóa từng mục một hay có cách nào tốt hơn?


1
Đối với lõi .NET, hãy kiểm tra câu trả lời này .
Makla

Câu trả lời:


61

Dispose MemoryCache hiện có và tạo một đối tượng MemoryCache mới.


3
Ban đầu, tôi sử dụng MemoryCache.Default, khiến cho việc Dispose khiến tôi hơi đau buồn. Tuy nhiên, Vứt bỏ cuối cùng vẫn là giải pháp tốt nhất mà tôi có thể tìm thấy. Cảm ơn.
LaustN

11
@LaustN bạn có thể nói rõ hơn về "nỗi đau" do MemoryCache.Default gây ra? Tôi hiện đang sử dụng MemoryCache.Default ... Tài liệu MemoryCache của MSDN khiến tôi băn khoăn không biết có nên xử lý và tạo lại hay không: "Không tạo các phiên bản MemoryCache trừ khi nó được yêu cầu. Nếu bạn tạo các phiên bản cache trong ứng dụng khách và ứng dụng Web, thì các phiên bản MemoryCache sẽ được tạo sớm trong vòng đời ứng dụng. " Điều này có áp dụng cho .Default không? Tôi không nói việc sử dụng Dispose là sai, thành thật mà nói, tôi chỉ muốn làm rõ tất cả những điều này.
ElonU Webdev

8
Nghĩ rằng điều đáng nói là Dispose không gọi bất kỳ CacheEntryRemovedCallbackđính kèm nào với các mục được lưu trong bộ nhớ cache hiện tại.
Mike Guthrie,

8
@ElonU: Câu trả lời Stack Overflow sau đây giải thích một số nỗi buồn mà bạn có thể gặp phải khi hủy bỏ phiên bản mặc định: stackoverflow.com/a/8043556/216440 . Để trích dẫn: "Trạng thái của bộ đệm được đặt để chỉ ra rằng bộ đệm được xử lý. Bất kỳ nỗ lực nào để gọi các phương thức bộ nhớ đệm công khai làm thay đổi trạng thái của bộ đệm, chẳng hạn như các phương pháp thêm, xóa hoặc truy xuất các mục nhập trong bộ đệm, có thể gây ra hành vi. Ví dụ: nếu bạn gọi phương thức Đặt sau khi bộ nhớ cache được xử lý, lỗi no-op sẽ xảy ra. "
Simon Tewsi

56

Vấn đề với liệt kê

Phần Ghi chú MemoryCache.GetEnumerator () cảnh báo: "Lấy một điều tra viên cho một cá thể MemoryCache là một hoạt động chặn và sử dụng nhiều tài nguyên. Do đó, điều tra viên không nên được sử dụng trong các ứng dụng sản xuất."

Đây là lý do tại sao , được giải thích trong mã giả của việc triển khai 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

Vì quá trình triển khai phân chia bộ nhớ cache trên nhiều đối tượng Từ điển, nên nó phải tập hợp mọi thứ lại với nhau thành một tập hợp duy nhất để giao lại một người điều tra. Mọi cuộc gọi tới GetEnumerator đều thực hiện quy trình sao chép đầy đủ được nêu chi tiết ở trên. Từ điển mới được tạo chứa các tham chiếu đến các đối tượng giá trị và khóa bên trong ban đầu, vì vậy các giá trị dữ liệu được lưu trong bộ nhớ cache thực tế của bạn không bị trùng lặp.

Cảnh báo trong tài liệu là chính xác. Tránh GetEnumerator () - bao gồm tất cả các câu trả lời ở trên sử dụng truy vấn LINQ.

Một giải pháp tốt hơn và linh hoạt hơn

Đây là một cách hiệu quả để xóa bộ nhớ cache mà chỉ đơn giản là xây dựng trên cơ sở hạ tầng giám sát thay đổi hiện có. Nó cũng cung cấp sự linh hoạt để xóa toàn bộ bộ nhớ cache hoặc chỉ một tập hợp con được đặt tên và không có vấn đề nào được thảo luận ở trên.

// 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
Có vẻ như một triển khai cho chức năng Vùng bị thiếu.
Jowen

Rất đẹp. Tôi đã cố gắng triển khai một cái gì đó bằng cách sử dụng các màn hình và guids bộ nhớ cache được xâu chuỗi nhưng nó bắt đầu trở nên hơi xấu khi tôi cố gắng thắt chặt chức năng.
Chao

7
Tôi không giới thiệu mẫu này để sử dụng chung. 1. Nó chậm, không có lỗi của việc thực hiện, nhưng phương pháp xử lý là cực kỳ chậm. 2. Nếu loại bỏ các mục của bạn khỏi bộ nhớ cache đã hết hạn, Màn hình thay đổi vẫn được gọi. 3. Máy của tôi đã nuốt toàn bộ CPU và mất một thời gian dài để xóa 30 nghìn mục khỏi bộ nhớ cache khi tôi đang chạy các bài kiểm tra hiệu suất. Một vài lần sau khi đợi hơn 5 phút, tôi chỉ giết các bài kiểm tra.
Aaron M

1
@PascalMathys Thật không may, không có giải pháp nào tốt hơn giải pháp này. Tôi đã kết thúc sử dụng nó, mặc dù có những nhược điểm, vì nó vẫn là một giải pháp tốt hơn so với sử dụng phép liệt kê.
Aaron M

9
@AaronM Giải pháp này có còn tốt hơn là chỉ xử lý bộ nhớ cache và tạo một bộ nhớ cache mới không?
RobSiklos

35

Từ http://connect.microsoft.com/VisualStudio/feedback/details/723620/memorycache-class-needs-a-clear-method

Cách giải quyết là:

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

33
Từ tài liệu : Lấy một bảng kê cho một cá thể MemoryCache là một hoạt động chặn và sử dụng nhiều tài nguyên. Do đó, máy điều tra không nên được sử dụng trong các ứng dụng sản xuất.
TrueWill

3
@emberdude Nó giống hệt như truy xuất một điều tra viên - bạn nghĩ việc triển khai Select()làm gì?
RobSiklos

1
Cá nhân, tôi đang sử dụng chức năng này trong kiểm tra đơn vị [TestInitialize] để xóa bộ nhớ đệm cho mỗi kiểm tra đơn vị. Nếu không, bộ nhớ cache vẫn tồn tại trong các bài kiểm tra đơn vị cho kết quả không mong muốn khi cố gắng so sánh hiệu suất giữa 2 chức năng.
Jacob Morrison

6
@JacobMorrison cho rằng, các bài kiểm tra đơn vị không phải là một "ứng dụng sản xuất" :)
Mels

1
@Mels cho rằng, các bài kiểm tra đơn vị nên được viết theo các tiêu chuẩn giống như "ứng dụng sản xuất"! :)
Etherman

21
var cacheItems = cache.ToList();

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

3
Điều này có cùng rủi ro như phản hồi của @ Tony; xin vui lòng xem bình luận của tôi dưới đó.
TrueWill

@TrueWill Ai là hoặc là @Tony?
Alex Angas

2
@AlexAngas - Anh ấy có thể đã đổi tên thành magritte. Xem thêm stackoverflow.com/questions/4183270/…
TrueWill

10

Nếu hiệu suất không phải là vấn đề thì một lớp lót tốt này sẽ thực hiện thủ thuật:

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


3

Bạn cũng có thể làm điều gì đó như sau:


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

3

Chạy qua điều này, và dựa trên nó, đã viết một phương pháp rõ ràng song song, hiệu quả hơn một chút:

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

1
Bạn đã kiểm tra nó để xem nó nhanh hơn (hay chậm hơn)?
Paul George,

1

Tôi chỉ quan tâm đến việc xóa bộ nhớ cache và thấy đây là một tùy chọn, khi sử dụng c # GlobalCachingProvider

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

0

một phiên bản cải tiến một chút của câu trả lời 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

Bạn có thể loại bỏ bộ nhớ cache MemoryCache.Default và sau đó đặt lại trường riêng tư singleton thành null, để làm cho nó tạo lại MemoryCache.Default.

       var field = typeof(MemoryCache).GetField("s_defaultCache",
            BindingFlags.Static |
            BindingFlags.NonPublic);
        field.SetValue(null, null);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.