繁体   English   中英

如何清除内存缓存?

[英]How to clear MemoryCache?

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

Dispose现有的 MemoryCache 并创建一个新的 MemoryCache 对象。

枚举的问题

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 都会执行上面详述的完整复制过程。 新创建的 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;
        }
    }
}

来自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);
}
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 不值得你花时间

https://connect.microsoft.com/VisualStudio/feedback/details/831755/memorycache-trim-method-doesnt-evict-100-of-the-items

如何清除 System.Runtime.Caching.MemoryCache

遇到了这个,并基于它,写了一个稍微更有效的并行清除方法:

    public void ClearAll()
    {
        var allKeys = _cache.Select(o => o.Key);
        Parallel.ForEach(allKeys, key => _cache.Remove(key));
    }

你也可以做这样的事情:


Dim _Qry = (From n In CacheObject.AsParallel()
           Select n).ToList()
For Each i In _Qry
    CacheObject.Remove(i.Key)
Next

您可以处理 MemoryCache.Default 缓存,然后将私有字段单例重新设置为 null,使其重新创建 MemoryCache.Default。

       var field = typeof(MemoryCache).GetField("s_defaultCache",
            BindingFlags.Static |
            BindingFlags.NonPublic);
        field.SetValue(null, null);

我只对清除缓存感兴趣,并在使用 c# GlobalCachingProvider 时发现这是一个选项

                var cache = GlobalCachingProvider.Instance.GetAllItems();
                if (dbOperation.SuccessLoadingAllCacheToDB(cache))
                {
                    cache.Clear();
                }

马格利特答案的改进版。

var cacheKeys = MemoryCache.Default.Where(kvp.Value is MyType).Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
{
    MemoryCache.Default.Remove(cacheKey);
}

此讨论也在此处进行: https://docs.microsoft.com/en-us/answers/answers/983399/view.html

我在那里写了一个答案,我会在这里转录:

using System.Collections.Generic;
using Microsoft.Extensions.Caching.Memory;
using ServiceStack;

public static class IMemoryCacheExtensions
{
    static readonly List<object> entries = new();

    /// <summary>
    /// Removes all entries, added via the "TryGetValueExtension()" method
    /// </summary>
    /// <param name="cache"></param>
    public static void Clear(this IMemoryCache cache)
    {
        for (int i = 0; i < entries.Count; i++)
        {
            cache.Remove(entries[i]);
        }
    }

    /// <summary>
    /// Use this extension method, to be able to remove all your entries later using "Clear()" method
    /// </summary>
    /// <typeparam name="TItem"></typeparam>
    /// <param name="cache"></param>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <returns></returns>
    public static bool TryGetValueExtension<TItem>(this IMemoryCache cache, object key, out TItem value)
    {
        entries.AddIfNotExists(key);

        if (cache.TryGetValue(key, out object result))
        {
            if (result == null)
            {
                value = default;
                return true;
            }

            if (result is TItem item)
            {
                value = item;
                return true;
            }
        }

        value = default;
        return false;
    }
}

暂无
暂无

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

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