【问题标题】:.Net Core MemoryCache PostEvictionCallback not working properly.Net Core MemoryCache PostEvictionCallback 无法正常工作
【发布时间】:2017-07-20 23:56:29
【问题描述】:

我在 Microsoft.Extensions.Caching.Memory.MemoryCache 中设置了具有滑动到期的缓存项。 我想在每次缓存项过期时触发回调,但是直到我在缓存中查询过期缓存项时才会触发回调。

代码如下:

using System;
using Microsoft.Extensions.Caching.Memory;

namespace Memcache
{
    public class Program
    {
        private static MemoryCache _cache;
        private static int _cacheExpSecs;

        public static void Main(string[] args)
        {
            _cache = new MemoryCache(new MemoryCacheOptions());
            _cacheExpSecs = 2;

            var cacheEntryOptions = new MemoryCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromSeconds(_cacheExpSecs))
            .RegisterPostEvictionCallback(callback: EvictionCallback);

            _cache.Set(1, "One", cacheEntryOptions);
            _cache.Set(2, "Two", cacheEntryOptions);

            var autoEvent = new System.Threading.AutoResetEvent(false);

            System.Threading.Timer timer = new System.Threading.Timer(checkCache, autoEvent, 1000, 6000);

            Console.Read();
        }

        private static void checkCache(Object o)
        {
            if(_cache.Get(1)!=null)
            {
                Console.WriteLine(string.Format(@"checkCache: Cache with key {0} will be removed manually and will trigger the callback.", 1));
                _cache.Remove(1);
            }
            else
            {
                Console.WriteLine(string.Format("checkCache: Cache with key {0} is expired.", 1));
            }


            if(_cache.Get(2) != null)
            {
                Console.WriteLine(string.Format("checkCache: Cache with key {0} will expire in {1} seconds, but won't trigger the callback until we check it's value again.", 2, _cacheExpSecs));
            }
            else
            {
                Console.WriteLine(string.Format("checkCache: Cache with key {0} is expired.", 2));
            }

        }

        private static void EvictionCallback(object key, object value, EvictionReason reason, object state)
        {
            Console.WriteLine();
            Console.WriteLine("/*****************************************************/");
            Console.WriteLine(string.Format("/*  EvictionCallback: Cache with key {0} has expired.  */", key));
            Console.WriteLine("/*****************************************************/");
            Console.WriteLine();
        }
    }
}

【问题讨论】:

    标签: .net caching asp.net-core memorycache


    【解决方案1】:

    要添加到接受答案和 cmets,您可以使用过期取消令牌强制缓存过期并自动驱逐。

    int expirationMinutes = 60;
    var expirationTime = DateTime.Now.Add(expirationMinutes);
    var expirationToken = new CancellationChangeToken(
        new CancellationTokenSource(TimeSpan.FromMinutes(expirationMinutes + .01)).Token);
    
    var cacheEntryOptions = new MemoryCacheEntryOptions()
             // Pin to cache.
             .SetPriority(CacheItemPriority.NeverRemove)
             // Set the actual expiration time
             .SetAbsoluteExpiration(expirationTime)
             // Force eviction to run
             .AddExpirationToken(expirationToken)
             // Add eviction callback
             .RegisterPostEvictionCallback(callback: CacheItemRemoved, state: this); 
    

    `

    缺少内置的计时器行为,旧的曾经有的,应该是设计使然,这是推荐的。见:https://github.com/aspnet/Caching/issues/248

    【讨论】:

    • 谢谢,这正是我想要的,并且可以确认不需要命中缓存来触发它。
    【解决方案2】:

    发生这种情况是因为在您查询该项目并检查过期之前,该项目不会被驱逐

    (来自MemoryCacheStore.Get(MemoryCacheKey key)的来源)

        internal MemoryCacheEntry Get(MemoryCacheKey key) {
            MemoryCacheEntry entry = _entries[key] as MemoryCacheEntry;
            // has it expired?
            if (entry != null && entry.UtcAbsExp <= DateTime.UtcNow) {
                Remove(key, entry, CacheEntryRemovedReason.Expired);
                entry = null;
            }
            // update outside of lock
            UpdateExpAndUsage(entry);
            return entry;
        }
    

    或者当Trim()由于内存压力在内部被调用时

    (来自TrimInternal(int percent)的来源)

    /*SNIP*/
            trimmedOrExpired = _expires.FlushExpiredItems(true);
            if (trimmedOrExpired < toTrim) {
                trimmed = _usage.FlushUnderUsedItems(toTrim - trimmedOrExpired);
                trimmedOrExpired += trimmed;
            }
    /*SNIP*/
    

    如果您的系统当前内存不足,无法触发修剪,那么项目将被驱逐的唯一时间是在尝试检索它们时。

    【讨论】:

    • 谢谢。 MemoryCacheOptions 中有 ExpirationScanFrequency 选项,但都不起作用。
    • 您现在可能不想接受我的回答。我完全忽略了这是 .NET Core。既然我的陈述仍然正确,请检查核心源
    • 好吧,逻辑还是一样的。 ExpirationScanFrequency 是在执行任何类型的 Get 或 Remove 后进行全面扫描的频率。如果时间比ExpirationScanFrequency 长,它会全面扫描项目,而不仅仅是它正在使用的项目,它仍然不会运行计时器来执行扫描,它们仍然会在执行操作时按需完成已执行。
    • 如果您愿意,您可以在自己的计时器上拨打_cache.Compact(0),这样会定期清除过期条目。
    • 谢谢,我将使用 Compact(0)。另一方面,在.NET框架中,System.Runtime.Caching.MemoryCache在滑动过期缓存项过期时触发回调。
    猜你喜欢
    • 2020-07-13
    • 2021-12-16
    • 2017-06-09
    • 1970-01-01
    • 1970-01-01
    • 2020-02-09
    • 1970-01-01
    • 2023-03-06
    相关资源
    最近更新 更多