.Net Core MemoryCache PostEvictionCallback无法正常工作

mat*_*lto 7 .net caching memorycache asp.net-core

我在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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*yon 13

要添加到接受答案和注释,您可以使用过期的取消令牌强制缓存过期并自动逐出.

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); 
Run Code Online (Sandbox Code Playgroud)

`

缺乏内置的计时器行为,旧的曾经有过,应该是设计的,这就是它所建议的.请参阅:https://github.com/aspnet/Caching/issues/248

  • 谢谢,这正是我正在寻找的,并且可以确认不需要命中缓存即可触发此操作。 (4认同)
  • 效果很好,仅供参考,必须在 aspnetcore 5+ 上将 `DateTime.Now.Add(...)` 更改为 `DateTime.Now.AddMinutes(...)`。 (2认同)

Sco*_*ain 9

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

(来自 的来源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;
    }
Run Code Online (Sandbox Code Playgroud)

或何时Trim()因内存压力而在内部调用

(来自 的来源TrimInternal(int percent)

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

如果您的系统当前的内存不够低,无法触发修剪,那么只有在尝试检索项目时才会驱逐项目。

  • 谢谢。MemoryCacheOptions 中有 ExpirationScanFrequency 选项,但都不起作用。 (2认同)
  • 如果您愿意,您可以在自己的计时器上调用“_cache.Compact(0)”,这将定期清除过期的条目。 (2认同)