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