如何清除内存缓存?
Posted
技术标签:
【中文标题】如何清除内存缓存?【英文标题】:How to clear MemoryCache? 【发布时间】:2011-05-10 03:44:45 【问题描述】:我已经使用 MemoryCache 类创建了一个缓存。我向其中添加了一些项目,但是当我需要重新加载缓存时,我想先清除它。最快的方法是什么?我应该遍历所有项目并一次删除它们还是有更好的方法?
【问题讨论】:
对于.NET核心检查this答案。 【参考方案1】:Dispose
现有的MemoryCache并创建一个新的MemoryCache对象。
【讨论】:
我最初使用的是 MemoryCache.Default,导致 Dispose 给我一些悲伤。尽管如此,Dispose 最终还是我能找到的最佳解决方案。谢谢。 @LaustN 您能否详细说明 MemoryCache.Default 造成的“悲伤”?我目前正在使用 MemoryCache.Default... MSDN 的 MemoryCache 文档让我想知道是否建议进行处置和重新创建:“除非需要,否则不要创建 MemoryCache 实例。如果您在客户端和 Web 应用程序中创建缓存实例,则 MemoryCache 实例应该在应用程序生命周期的早期创建。”这是否适用于 .Default?我并不是说使用 Dispose 是错误的,老实说,我只是想澄清这一切。 值得一提的是Dispose
确实调用附加到当前缓存项的任何CacheEntryRemovedCallback
。
@ElonU:以下 Stack Overflow 答案解释了您在处理默认实例时可能遇到的一些问题:***.com/a/8043556/216440。引用:“缓存的状态设置为指示缓存已被释放。任何尝试调用更改缓存状态的公共缓存方法(例如添加、删除或检索缓存条目的方法)都可能导致意外行为。例如,如果在释放缓存后调用 Set 方法,则会发生 no-op 错误。"【参考方案2】:
枚举的问题
MemoryCache.GetEnumerator() Remarks section 警告:“检索 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 都会执行上面详述的完整复制过程。新创建的 Dictionary 包含对原始内部键和值对象的引用,因此您的实际缓存数据值不会重复。
文档中的警告是正确的。避免 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;
【讨论】:
似乎是缺少区域功能的实现。 非常好。我一直在尝试使用链式内存缓存监视器和 guid 来实现一些东西,但是当我试图加强功能时,它开始变得有点难看。 我不建议将此模式用于一般用途。 1.它的速度很慢,没有执行的错误,但是dispose方法非常慢。 2. 如果您从缓存中驱逐项目过期,更改监视器仍会被调用。 3. 当我运行性能测试时,我的机器正在吞噬所有的 CPU,并且需要很长时间才能从缓存中清除 30k 项。等了 5 多分钟后,有几次我刚刚终止了测试。 @PascalMathys 不幸的是,没有比这更好的解决方案了。尽管有缺点,但我最终还是使用了它,因为它仍然是比使用枚举更好的解决方案。 @AaronM 这个解决方案是否仍然比仅仅处理缓存并实例化一个新的更好?【参考方案3】:来自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);
【讨论】:
来自documentation:检索 MemoryCache 实例的枚举器是一项资源密集型和阻塞操作。因此,枚举器不应在生产应用程序中使用。 @emberdude 这和检索枚举数完全一样——你觉得Select()
的实现是做什么的?
就个人而言,我在我的单元测试 [TestInitialize] 函数中使用它来清除每个单元测试的内存缓存。否则,缓存会在单元测试中持续存在,在尝试比较两个函数之间的性能时会产生意想不到的结果。
@JacobMorrison 可以说,单元测试不是“生产应用程序”:)
@Mels 可以说,单元测试应该按照与“生产应用程序”相同的标准编写! :)【参考方案4】:
var cacheItems = cache.ToList();
foreach (KeyValuePair<String, Object> a in cacheItems)
cache.Remove(a.Key);
【讨论】:
这与@Tony 的回复具有相同的风险;请参阅我的评论。 @TrueWill @Tony 是谁? @AlexAngas - 他可能已经把名字改成了马格利特。另见***.com/questions/4183270/…【参考方案5】:如果性能不是问题,那么这个漂亮的单线就可以解决问题:
cache.ToList().ForEach(a => cache.Remove(a.Key));
【讨论】:
【参考方案6】:好像有Trim方法。
所以要清除所有你要做的内容
cache.Trim(100)
编辑: 在挖掘了更多之后,似乎研究 Trim 不值得你花时间
https://connect.microsoft.com/VisualStudio/feedback/details/831755/memorycache-trim-method-doesnt-evict-100-of-the-items
How do I clear a System.Runtime.Caching.MemoryCache
【讨论】:
【参考方案7】:碰到这个,在此基础上,写了一个稍微有效的并行清除方法:
public void ClearAll()
var allKeys = _cache.Select(o => o.Key);
Parallel.ForEach(allKeys, key => _cache.Remove(key));
【讨论】:
您是否测试过它是否更快(或更慢)?【参考方案8】:你也可以这样做:
Dim _Qry = (From n In CacheObject.AsParallel()
Select n).ToList()
For Each i In _Qry
CacheObject.Remove(i.Key)
Next
【讨论】:
【参考方案9】:您可以释放 MemoryCache.Default 缓存,然后将私有字段单例重新设置为 null,以使其重新创建 MemoryCache.Default。
var field = typeof(MemoryCache).GetField("s_defaultCache",
BindingFlags.Static |
BindingFlags.NonPublic);
field.SetValue(null, null);
【讨论】:
【参考方案10】:我只对清除缓存感兴趣,并在使用 c# GlobalCachingProvider 时发现这是一个选项
var cache = GlobalCachingProvider.Instance.GetAllItems();
if (dbOperation.SuccessLoadingAllCacheToDB(cache))
cache.Clear();
【讨论】:
【参考方案11】:magritte answer 的改进版本。
var cacheKeys = MemoryCache.Default.Where(kvp.Value is MyType).Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
MemoryCache.Default.Remove(cacheKey);
【讨论】:
以上是关于如何清除内存缓存?的主要内容,如果未能解决你的问题,请参考以下文章