繁体   English   中英

如何清除System.Runtime.Caching.MemoryCache

[英]How do I clear a System.Runtime.Caching.MemoryCache

我使用System.Runtime.Caching.MemoryCache来保存永不过期的项目。 但是,有时我需要能够清除整个缓存。 我怎么做?

在这里问了一个类似的问题,我是否可以枚举缓存,但这是一个坏主意,因为它需要在枚举期间进行同步。

我尝试过使用.Trim(100)但这根本不起作用。

我已经尝试通过Linq获取所有密钥的列表,但后来我回到了我开始的地方,因为一个接一个地逐出项目很容易导致竞争条件。

我想存储所有的密钥,然后为每个.Remove(key)发出一个.Remove(key) ,但是那里也有一个隐含的竞争条件,所以我需要锁定对密钥列表的访问权限,事情再次变得混乱。

然后我认为我应该能够在整个缓存上调用.Dispose() ,但由于它的实现方式,我不确定这是否是最好的方法。

使用ChangeMonitors不是我的设计的选项,并且对于这样一个微不足道的要求是不必要的。

那么,我该如何彻底清除缓存呢?

起初我正在努力解决这个问题。 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);
}

如果您希望能够再使用它,则不应在MemoryCache的Default成员上调用dispose:

设置高速缓存的状态以指示高速缓存被释放。 任何调用更改缓存状态的公共缓存方法的尝试(例如添加,删除或检索缓存条目的方法)都可能导致意外行为。 例如,如果在释放缓存后调用Set方法,则会发生no-op错误。 如果您尝试从缓存中检索项目,Get方法将始终返回Nothing。 http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.dispose.aspx

关于Trim,它应该工作:

Trim属性首先删除超过绝对或滑动到期的条目。 为已删除的项目注册的任何回调都将传递已删除的已过期原因。

如果删除过期的条目不足以达到指定的修剪百分比,则将根据最近最少使用(LRU)算法从缓存中删除其他条目,直到达到请求的修剪百分比。

但另外两个用户报告它在同一页面上无法工作,所以我猜你会被删除() http://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache.trim.aspx

更新然而,我没有看到它是单例或其他不安全的多个实例,所以你应该能够覆盖你的引用。

但是,如果您需要从Default实例中释放内存,则必须手动清除它或通过dispose永久销毁它(使其无法使用)。

基于你的问题,你可以让你自己的单例强制类返回一个你可以随意内部处理的Memorycache。作为缓存的本质:-)

这就是我为我正在做的事情所做的......

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

我知道这是一个古老的问题,但我遇到的最好的选择是

处理现有的MemoryCache并创建一个新的MemoryCache对象。 https://stackoverflow.com/a/4183319/880642

答案并没有真正提供以线程安全方式执行此操作的代码。 但这可以使用Interlocked.Exchange来实现

var oldCache = Interlocked.Exchange(ref _existingCache, new MemoryCache("newCacheName"));
oldCache.Dispose();

这将使用新缓存交换现有缓存,并允许您在原始缓存上安全地调用Dispose。 这避免了需要枚举缓存中的项目以及在使用缓存时导致的竞争条件。

@ stefan的答案详细说明了原理; 这是我怎么做的。

应该在重新创建缓存时同步对缓存的访问,以避免客户端代码在处置之后,但在重新创建之前访问缓存的竞争条件。

要避免此同步,请在适配器类(包装MemoryCache)中执行此操作:

public void clearCache() {
  var oldCache = TheCache;
  TheCache = new MemoryCache("NewCacheName", ...);
  oldCache.Dispose();
  GC.Collect();
}

这样, TheCache始终处于非处置状态,不需要同步。

我也遇到了这个问题。 .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
    }
}

看看这篇文章,特别是Thomas F. Abraham发布的答案。 它有一个解决方案,使您可以清除整个缓存或命名子集。

这里的关键是:

// Cache objects are obligated to remove entry upon change notification.
base.OnChanged(null);

我自己实现了这一点,一切似乎都很好。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM