【问题标题】:How to clear MemoryCache?如何清除内存缓存?
【发布时间】:2011-05-10 03:44:45
【问题描述】:

我已经使用 MemoryCache 类创建了一个缓存。我向其中添加了一些项目,但是当我需要重新加载缓存时,我想先清除它。最快的方法是什么?我应该遍历所有项目并一次删除它们还是有更好的方法?

【问题讨论】:

  • 对于.NET核心检查this答案。

标签: c# caching memory .net-4.0 memorycache


【解决方案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 答案解释了您在处理默认实例时可能遇到的一些问题:stackoverflow.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 - 他可能已经把名字改成了马格利特。另见stackoverflow.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);
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2015-03-31
              • 1970-01-01
              • 2021-05-30
              • 2012-10-07
              • 2022-12-20
              • 2016-02-20
              • 2023-03-04
              相关资源
              最近更新 更多