imemorycache.set() 的过期回调不触发是因为它仅在条目被实际移除时执行,而非绝对过期时间到达时;需配合evictionreason.expired判断,并用定时器扫描或ihostedservice实现准实时清理。

IMemoryCache.Set() 的过期回调为什么没触发
因为 IMemoryCache 默认不支持“过期时回调”,Set() 方法里传的 PostEvictionCallbacks 只在**条目被显式移除、内存压力触发淘汰、或主动调用 Remove() 时才执行**,而不是在绝对过期时间(AbsoluteExpiration)一到就调用。这是最常被误解的一点。
常见错误现象:PostEvictionCallback 完全不执行,或只在手动 cache.Remove(key) 后才触发,但缓存明明已过期却没反应。
- 必须配合
EvictionReason判断是否为过期导致(EvictionReason.Expired),不能只依赖回调发生就认为“已过期” -
AbsoluteExpirationRelativeToNow和SlidingExpiration都会触发该回调——但前提是该条目**实际被清理了**,而不仅仅是“逻辑过期” - 缓存项可能长期驻留内存:若未发生内存压力、无新写入、也无主动访问触发滑动刷新,它哪怕过了
AbsoluteExpiration也不会被立刻回收
想真正“定时清理”,得自己加一层调度
靠 IMemoryCache 自身无法实现严格按时执行的清理动作,必须引入外部定时器驱动周期性扫描或预清除。推荐用 System.Threading.Timer 或 IHostedService + IScheduledTask(.NET 8+)。
使用场景:需要每分钟检查一次过期缓存并释放资源(如关闭数据库连接、删除临时文件)、或上报过期统计。
- 不要用
Task.Delay().Wait()阻塞线程,会拖垮服务 - 避免在回调中直接调用
cache.Get()—— 此时项可能已被移除,应通过evictionReason和removedValue参数获取上下文 - 如果清理逻辑耗时(如 IO 操作),务必用
Task.Run()脱离主线程,否则阻塞缓存内部锁
示例节选(定时扫描):
var timer = new Timer(_ => {
foreach (var key in cache.Keys.ToList()) { // 必须 ToList() 避免枚举时修改
if (cache.TryGetValue(key, out var val)) continue; // 仍可取到 → 未过期/未淘汰
// 这里可记录日志或触发清理逻辑
Console.WriteLine($"Key {key} is evicted");
}
}, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
PostEvictionCallback 中能安全做什么
回调函数运行在缓存内部线程池上下文中,且持有缓存锁,所以操作必须极轻量。它的本质是“通知你某条数据没了”,不是“给你机会续命”。
- ✅ 安全:记录日志、发监控事件、释放托管资源(如
Dispose()实现的对象) - ⚠️ 风险:调用
cache.Set()或cache.GetOrCreate()—— 可能死锁或递归触发回调 - ❌ 禁止:同步等待 IO(如
File.Delete())、阻塞线程(Thread.Sleep())、访问 HttpContext(ASP.NET Core 中回调不在请求上下文)
若需在过期后重建缓存,应在业务层(如 Controller 或 Service)捕获 Get() 返回 null 后再 GetOrCreate(),而不是在回调里重设。
替代方案:用 MemoryCacheEntryOptions 设置更精准的生命周期策略
虽然不能解决“回调时机不准”,但合理组合参数能大幅减少手动干预需求:
- 同时设置
AbsoluteExpirationRelativeToNow和SlidingExpiration:前者兜底防长驻,后者保活跃 - 用
Size+MaximumSize触发基于容量的淘汰,比纯时间更可控(尤其对大对象缓存) - .NET 6+ 支持
ExpiryTokens(IChangeToken),可绑定自定义过期条件(如配置变更、文件更新),比时间更灵活
注意:Size 不是字节数,而是你指定的抽象单位;若未设 Size,则 MaximumSize 无效。
真正难处理的是跨服务共享缓存失效、或缓存项持有非托管资源(如 FileStream)。这时候光靠 IMemoryCache 不够,得结合 IDisposable + 回调 + 外部健康检查。











