我使用System.Runtime.Caching.MemoryCache
来保存永不过期的项目。但是,有时我需要能够清除整个缓存。我该怎么做?
我问了一个类似的问题here,关于我是否可以枚举缓存,但这是一个坏主意,因为它需要在枚举期间进行同步。
我尝试使用.Trim(100)
,但这根本不起作用。
我尝试通过Linq获取所有密钥的列表,但后来我回到了我开始的地方,因为逐个逐出项目很容易导致竞争条件。
我想存储所有密钥,然后为每个密钥发出.Remove(key)
,但是那里也存在隐含的竞争条件,所以我需要锁定对密钥列表和事物的访问权限再次变得混乱。
然后我认为我应该可以在整个缓存上调用.Dispose()
,但我不确定这是否是最好的方法,因为它的实现方式。
使用ChangeMonitors
不是我的设计的选项,并且对于这样一个微不足道的要求是不必要的。
那么,我该如何完全清除缓存?
答案 0 :(得分:31)
起初我正在努力解决这个问题。 MemoryCache.Default.Trim(100)不起作用(如上所述)。修剪是最佳尝试,因此如果缓存中有100个项目,并且您调用Trim(100)它将删除最少使用的项目。
修剪会返回已移除的项目数,大多数人都希望删除所有项目。
此代码在MemoryCache.Default的xUnit测试中为我删除了MemoryCache中的所有项目。 MemoryCache.Default是默认的Region。
foreach (var element in MemoryCache.Default)
{
MemoryCache.Default.Remove(element.Key);
}
答案 1 :(得分:16)
如果您希望能够再使用它,则不应在MemoryCache的默认成员上调用dispose:
设置缓存的状态以指示缓存已被释放。 任何尝试调用改变状态的公共缓存方法 缓存,例如添加,删除或检索缓存的方法 条目,可能会导致意外行为。例如,如果你打电话给 放置缓存后设置方法,发生无操作错误。如果你 尝试从缓存中检索项目,Get方法将始终 什么也没有。 http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.dispose.aspx
关于Trim,它应该起作用:
Trim属性首先删除超过绝对或滑动过期的条目。任何已注册的回调 对于已删除的项目,将传递已删除的已过期原因。
如果删除过期的条目不足以达到指定的修剪百分比,则将从缓存中删除其他条目 基于最近最少使用(LRU)算法直到请求 达到修剪百分比。
但其他两位用户报告说它在同一页面上无法正常工作,所以我猜你会遇到Remove()http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.trim.aspx
更新 但是我没有看到它是单例或者不安全的多个实例,所以你应该能够覆盖你的引用。
但是如果您需要从Default实例中释放内存,则必须手动清除它或通过dispose永久销毁它(使其无法使用)。
根据你的问题,你可以让自己的单例强制类返回一个你可以随意处理的Memorycache。作为缓存的本质: - )
答案 2 :(得分:8)
这就是我为我正在做的事情所做的......
public void Flush()
{
List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
{
MemoryCache.Default.Remove(cacheKey);
}
}
答案 3 :(得分:2)
@ stefan的详细解答详细说明了这个原则;这是我怎么做的。
应该在重新创建缓存时同步对缓存的访问,以避免客户端代码在处理缓存后但在重新创建之前访问缓存的竞争条件。
要避免此同步,请在适配器类(包装MemoryCache)中执行此操作:
public void clearCache() {
var oldCache = TheCache;
TheCache = new MemoryCache("NewCacheName", ...);
oldCache.Dispose();
GC.Collect();
}
这样,TheCache
始终处于非处置状态,不需要同步。
答案 4 :(得分:1)
我知道这是一个老问题,但是我遇到的最好的选择是
处置现有的MemoryCache并创建一个新的MemoryCache对象。 https://stackoverflow.com/a/4183319/880642
答案并没有真正提供以线程安全的方式执行此操作的代码。但这可以使用Interlocked.Exchange
来实现var oldCache = Interlocked.Exchange(ref _existingCache, new MemoryCache("newCacheName"));
oldCache.Dispose();
这会将现有缓存替换为新的缓存,并允许您安全地在原始缓存上调用Dispose。这避免了需要枚举缓存中的项目以及由于在使用缓存时放置缓存而导致的竞争条件。
答案 5 :(得分:0)
我也遇到了这个问题。 .Dispose()做了一些与我的预期完全不同的事情。
相反,我在控制器类中添加了一个静态字段。我没有使用默认缓存来解决这种行为,但创建了一个私有缓存(如果你想调用它)。所以我的实现看起来有点像这样:
public class MyController : Controller
{
static MemoryCache s_cache = new MemoryCache("myCache");
public ActionResult Index()
{
if (conditionThatInvalidatesCache)
{
s_cache = new MemoryCache("myCache");
}
String s = s_cache["key"] as String;
if (s == null)
{
//do work
//add to s_cache["key"]
}
//do whatever next
}
}
答案 6 :(得分:0)
查看this帖子,特别是Thomas F. Abraham发布的答案。 它有一个解决方案,使您可以清除整个缓存或命名子集。
这里的关键是:
// Cache objects are obligated to remove entry upon change notification.
base.OnChanged(null);
我自己实现了这一点,而且一切似乎都运行良好。