简体   繁体   English

如何清除内存缓存?

[英]How to clear MemoryCache?

I have created a cache using the MemoryCache class.我使用 MemoryCache class 创建了一个缓存。 I add some items to it but when I need to reload the cache I want to clear it first.我向其中添加了一些项目,但是当我需要重新加载缓存时,我想先清除它。 What is the quickest way to do this?最快的方法是什么? Should I loop through all the items and remove them one at a time or is there a better way?我应该遍历所有项目并一次删除它们还是有更好的方法?

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

The problem with enumeration枚举的问题

The MemoryCache.GetEnumerator() Remarks section warns: "Retrieving an enumerator for a MemoryCache instance is a resource-intensive and blocking operation. Therefore, the enumerator should not be used in production applications." MemoryCache.GetEnumerator() 备注部分警告:“检索 MemoryCache 实例的枚举器是一种资源密集型和阻塞操作。因此,不应在生产应用程序中使用枚举器。”

Here's why , explained in pseudocode of the GetEnumerator() implementation:这就是原因,在 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

Since the implementation splits the cache across multiple Dictionary objects, it must bring everything together into a single collection in order to hand back an enumerator.由于实现将缓存拆分为多个 Dictionary 对象,因此它必须将所有内容整合到一个集合中,以便交回一个枚举器。 Every call to GetEnumerator executes the full copy process detailed above.每次调用 GetEnumerator 都会执行上面详述的完整复制过程。 The newly created Dictionary contains references to the original internal key and value objects, so your actual cached data values are not duplicated.新创建的 Dictionary 包含对原始内部键和值对象的引用,因此您的实际缓存数据值不会重复。

The warning in the documentation is correct.文档中的警告是正确的。 Avoid GetEnumerator() -- including all of the answers above that use LINQ queries.避免使用 GetEnumerator() —— 包括上面使用 LINQ 查询的所有答案。

A better and more flexible solution更好更灵活的解决方案

Here's an efficient way of clearing the cache that simply builds on the existing change monitoring infrastructure.这是一种清除缓存的有效方法,它只是建立在现有的更改监控基础架构上。 It also provides the flexibility to clear either the entire cache or just a named subset and has none of the problems discussed above.它还提供了清除整个缓存或仅清除命名子集的灵活性,并且没有上述问题。

// 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;
        }
    }
}

From http://connect.microsoft.com/VisualStudio/feedback/details/723620/memorycache-class-needs-a-clear-method来自http://connect.microsoft.com/VisualStudio/feedback/details/723620/memorycache-class-needs-a-clear-method

The workaround is:解决方法是:

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));

It seems that there is a Trim method.似乎有一种Trim方法。

So to clear all contents you'd just do所以要清除所有你要做的内容

cache.Trim(100)

EDIT: after digging some more, it seems that looking into Trim is not worth your time编辑:在挖掘更多之后,看起来研究 Trim 不值得你花时间

https://connect.microsoft.com/VisualStudio/feedback/details/831755/memorycache-trim-method-doesnt-evict-100-of-the-items 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 如何清除 System.Runtime.Caching.MemoryCache

Ran across this, and based on it, wrote a slightly more effective, parallel clear method:遇到了这个,并基于它,写了一个稍微更有效的并行清除方法:

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

You could also do something like this:你也可以做这样的事情:


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

You can dispose the MemoryCache.Default cache and then re-set the private field singleton to null, to make it recreate the MemoryCache.Default.您可以处理 MemoryCache.Default 缓存,然后将私有字段单例重新设置为 null,使其重新创建 MemoryCache.Default。

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

I was only interested in clearing the cache and found this as an option, when using the c# GlobalCachingProvider我只对清除缓存感兴趣,并在使用 c# GlobalCachingProvider 时发现这是一个选项

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

a bit improved version of 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);
}

This discussion is also being done here: https://docs.microsoft.com/en-us/answers/answers/983399/view.html此讨论也在此处进行: https://docs.microsoft.com/en-us/answers/answers/983399/view.html

I wrote an answer there and I'll transcribe it here:我在那里写了一个答案,我会在这里转录:

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