.Net Core MemoryCache PostEvictionCallback 无法正常工作
Posted
技术标签:
【中文标题】.Net Core MemoryCache PostEvictionCallback 无法正常工作【英文标题】:.Net Core MemoryCache PostEvictionCallback not working properly 【发布时间】: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();
【问题讨论】:
【参考方案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在滑动过期缓存项过期时触发回调。以上是关于.Net Core MemoryCache PostEvictionCallback 无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章
.Net Core MemoryCache PostEvictionCallback 无法正常工作
.NET Core 2.0迁移技巧之MemoryCache问题修复