MemoryCache的CacheItemPolicy仅在固定20秒边界过期问题问询
问题说明
之前靠Ben Hall的方案搞定了FileSystemWatcher事件重复触发的问题,但现在碰到个新麻烦:用CacheItemPolicy设置了AbsoluteExpiration或SlidingExpiration的MemoryCache,根本不按设定的时间过期,只会在固定的20秒时间点(比如整分的:00、:20、:40)触发过期回调。举个例子,我设置绝对过期为当前时间加100毫秒,结果回调OnRemoveFromCache要等到下一个20秒边界才执行——比如事件12:34:05.000触发,回调12:34:20.000才跑起来。查了才知道,这是MemoryCache本身的过期粒度就是20秒导致的,现在想找个类似模式但粒度更细的替代方案,或者是不是得自己实现一个?
可行方案
1. 换用官方的ASP.NET Core内存缓存
直接用Microsoft.Extensions.Caching.Memory,这是ASP.NET Core里的内存缓存实现,支持毫秒级的过期精度,API和传统MemoryCache差不多,迁移成本很低。用MemoryCacheEntryOptions设置AbsoluteExpirationRelativeToNow或者SlidingExpiration,回调能精准触发,完全满足需求。
2. 用第三方缓存库
比如CacheManager,这是个开源的缓存抽象库,支持多种缓存后端,内存缓存模式下可以配置更细的过期检查间隔,轻松实现毫秒级的过期触发。
3. 自己写个轻量级缓存
要是不想加第三方依赖,自己写个简单的也不难:
- 用字典存缓存项,每个项记录键、值、过期时间和回调函数。
- 启动一个后台定时任务(比如用
Timer或者循环Task.Delay),每隔几毫秒检查一次缓存项,到过期时间就触发回调并移除。 - 注意线程安全,操作字典的时候加锁就行。
给个简单的示例代码:
public class PreciseCache { private readonly Dictionary<string, CacheEntry> _cacheStore = new(); private readonly object _lock = new(); private readonly Timer _cleanupTimer; public PreciseCache(int checkIntervalMs = 10) { _cleanupTimer = new Timer(CleanupExpiredEntries, null, 0, checkIntervalMs); } public void Add(string key, object value, TimeSpan absoluteExpiration, Action<string, object> onExpireCallback) { lock (_lock) { _cacheStore[key] = new CacheEntry { Value = value, ExpiresAt = DateTime.UtcNow.Add(absoluteExpiration), OnExpire = onExpireCallback }; } } private void CleanupExpiredEntries(object state) { var now = DateTime.UtcNow; List<string> expiredKeys; lock (_lock) { expiredKeys = _cacheStore.Where(kv => kv.Value.ExpiresAt <= now).Select(kv => kv.Key).ToList(); } foreach (var key in expiredKeys) { lock (_lock) { if (_cacheStore.TryRemove(key, out var entry)) { entry.OnExpire?.Invoke(key, entry.Value); } } } } private class CacheEntry { public object Value { get; set; } public DateTime ExpiresAt { get; set; } public Action<string, object> OnExpire { get; set; } } }
4. 别碰修改MemoryCache内部参数的方案
有人可能会想通过反射改MemoryCache的过期检查间隔,但这属于黑科技,完全依赖内部实现,.NET版本一更新就可能失效,生产环境绝对别用。
总结
优先选Microsoft.Extensions.Caching.Memory,官方维护靠谱,精度够还不用改太多代码;其次考虑第三方库;实在不行自己写个轻量级的也简单。
内容的提问来源于stack exchange,提问作者lmattcris

