답변:
Dispose
기존 MemoryCache를 만들고 새 MemoryCache 개체를 만듭니다.
Dispose
것은 언급 할 가치가 있다고 생각했습니다 CacheEntryRemovedCallback
.
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
구현은 캐시를 여러 Dictionary 개체로 분할하므로 열거자를 다시 전달하려면 모든 것을 단일 컬렉션으로 가져와야합니다. GetEnumerator에 대한 모든 호출은 위에서 설명한 전체 복사 프로세스를 실행합니다. 새로 생성 된 사전에는 원래 내부 키 및 값 개체에 대한 참조가 포함되어 있으므로 실제 캐시 된 데이터 값이 중복되지 않습니다.
문서의 경고가 정확합니다. LINQ 쿼리를 사용하는 위의 모든 답변을 포함하여 GetEnumerator ()를 사용하지 마십시오.
다음은 기존 변경 모니터링 인프라를 기반으로하는 캐시를 효율적으로 지우는 방법입니다. 또한 전체 캐시 또는 명명 된 하위 집합을 지울 수있는 유연성을 제공하며 위에서 설명한 문제가 없습니다.
// 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;
}
}
}
해결 방법은 다음과 같습니다.
List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
{
MemoryCache.Default.Remove(cacheKey);
}
Select()
이 무엇을하나요?
var cacheItems = cache.ToList();
foreach (KeyValuePair<String, Object> a in cacheItems)
{
cache.Remove(a.Key);
}
성능이 문제가되지 않는다면이 멋진 한 줄짜리가 트릭을 할 것입니다.
cache.ToList().ForEach(a => cache.Remove(a.Key));
Trim 방법 이있는 것 같습니다 .
따라서 모든 내용을 지우려면
cache.Trim(100)
편집 : 좀 더 파고 나면 Trim을 들여다 보는 것이 시간 가치가없는 것 같습니다.
이것을 살펴보고 그것을 기반으로 약간 더 효과적인 병렬 클리어 방법을 작성했습니다.
public void ClearAll()
{
var allKeys = _cache.Select(o => o.Key);
Parallel.ForEach(allKeys, key => _cache.Remove(key));
}