如何清除内存缓存?

发布于 2024-10-02 02:08:11 字数 99 浏览 7 评论 0原文

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

I have created a cache using the 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

翻了热茶 2024-10-09 02:08:12

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

Dispose the existing MemoryCache and create a new MemoryCache object.

居里长安 2024-10-09 02:08:12

枚举问题

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 都会执行上面详述的完整复制过程。新创建的字典包含对原始内部键和值对象的引用,因此实际缓存的数据值不会重复。

文档中的警告是正确的。避免 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;
        }
    }
}

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."

Here's why, explained in pseudocode of the GetEnumerator() implementation:

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. Every call to GetEnumerator executes the full copy process detailed above. The newly created Dictionary contains references to the original internal key and value objects, so your actual cached data values are not duplicated.

The warning in the documentation is correct. Avoid GetEnumerator() -- including all of the answers above that use LINQ queries.

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;
        }
    }
}
︶ ̄淡然 2024-10-09 02:08:12

来自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);
}

From 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);
}
[旋木] 2024-10-09 02:08:12
var cacheItems = cache.ToList();

foreach (KeyValuePair<String, Object> a in cacheItems)
{
    cache.Remove(a.Key);
}
var cacheItems = cache.ToList();

foreach (KeyValuePair<String, Object> a in cacheItems)
{
    cache.Remove(a.Key);
}
放手` 2024-10-09 02:08:12

如果性能不是问题,那么这个漂亮的单行代码就可以解决问题:

cache.ToList().ForEach(a => cache.Remove(a.Key));

If performance isn't an issue then this nice one-liner will do the trick:

cache.ToList().ForEach(a => cache.Remove(a.Key));
停顿的约定 2024-10-09 02:08:12

似乎有一个 修剪方法。

因此,要清除所有内容,您只需

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

It seems that there is a Trim method.

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

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

无声无音无过去 2024-10-09 02:08:12

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

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

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));
    }
但可醉心 2024-10-09 02:08:12

你也可以这样做:


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

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
美煞众生 2024-10-09 02:08:12

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

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

You can dispose the MemoryCache.Default cache and then re-set the private field singleton to null, to make it recreate the MemoryCache.Default.

       var field = typeof(MemoryCache).GetField("s_defaultCache",
            BindingFlags.Static |
            BindingFlags.NonPublic);
        field.SetValue(null, null);
烧了回忆取暖 2024-10-09 02:08:12

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

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

I was only interested in clearing the cache and found this as an option, when using the c# GlobalCachingProvider

                var cache = GlobalCachingProvider.Instance.GetAllItems();
                if (dbOperation.SuccessLoadingAllCacheToDB(cache))
                {
                    cache.Clear();
                }
美男兮 2024-10-09 02:08:12

我使用 IMemeryCache 在我的控制器中添加 DI 容器,并使用此代码来清除缓存:

  private readonly IMemoryCache _memoryCache;
    public AdminController(IMemoryCache memoryCache) 
    { 
        _memoryCache = memoryCache;
    }

    [HttpGet("ClearCache")]
    public IActionResult ClearCache()
    {
        ClearMemoryCache();

        return Ok();
    }

    private  void ClearMemoryCache()
    {
        var cacheImplType = typeof(MemoryCache);
        var entriesField = cacheImplType.GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance);
        var entries = (IDictionary)entriesField?.GetValue(_memoryCache);
        entries?.Clear();
    }

它对我有用。

I use IMomeryCache that add with DI container in my controller and I use this code for clear cache:

  private readonly IMemoryCache _memoryCache;
    public AdminController(IMemoryCache memoryCache) 
    { 
        _memoryCache = memoryCache;
    }

    [HttpGet("ClearCache")]
    public IActionResult ClearCache()
    {
        ClearMemoryCache();

        return Ok();
    }

    private  void ClearMemoryCache()
    {
        var cacheImplType = typeof(MemoryCache);
        var entriesField = cacheImplType.GetField("_entries", BindingFlags.NonPublic | BindingFlags.Instance);
        var entries = (IDictionary)entriesField?.GetValue(_memoryCache);
        entries?.Clear();
    }

its worked for me.

心安伴我暖 2024-10-09 02:08:12

马格利特答案的一点改进版本。

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

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);
}
您的好友蓝忘机已上羡 2024-10-09 02:08:12

此讨论也在这里进行:
https://learn.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]);
        }
        entries.Clear();
    }

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

This discussion is also being done here:
https://learn.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]);
        }
        entries.Clear();
    }

    /// <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;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文