C# MemoryCache绝对过期行为奇怪
我试图在.net 4.5中使用C# MemoryCache绝对过期行为奇怪,c#,.net,memorycache,C#,.net,Memorycache,我试图在.net 4.5中使用MemoryCache来跟踪并自动更新各种项目,但似乎无论我将什么设置为绝对过期它都只会在15秒或更长时间内过期 我希望缓存项每5秒过期一次,但它总是在至少15秒后过期,如果我将过期时间移出,它将结束为15秒+我的刷新间隔,但决不少于15秒 是否有一些我没有看到的内部计时器分辨率?我浏览了一些反映的系统、运行时、缓存、MemoryCache代码,没有任何突出之处,我在互联网上找不到任何其他人有这个问题 下面我有一个非常基本的例子来说明这个问题 我想让CacheEnt
MemoryCache
来跟踪并自动更新各种项目,但似乎无论我将什么设置为绝对过期
它都只会在15秒或更长时间内过期
我希望缓存项每5秒过期一次,但它总是在至少15秒后过期,如果我将过期时间移出,它将结束为15秒+我的刷新间隔,但决不少于15秒
是否有一些我没有看到的内部计时器分辨率?我浏览了一些反映的系统、运行时、缓存、MemoryCache
代码,没有任何突出之处,我在互联网上找不到任何其他人有这个问题
下面我有一个非常基本的例子来说明这个问题
我想让CacheEntryUpdate
每隔5秒左右被点击一次,然后用新数据更新,但是,正如我所说,它只在15秒以上被点击
static MemoryCache MemCache;
static int RefreshInterval = 5000;
protected void Page_Load(object sender, EventArgs e)
{
if (MemCache == null)
MemCache = new MemoryCache("MemCache");
if (!MemCache.Contains("cacheItem"))
{
var cacheObj = new object();
var policy = new CacheItemPolicy
{
UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate),
AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval)
};
var cacheItem = new CacheItem("cacheItem", cacheObj);
MemCache.Set("cacheItem", cacheItem, policy);
}
}
private void CacheEntryUpdate(CacheEntryUpdateArguments args)
{
var cacheItem = MemCache.GetCacheItem(args.Key);
var cacheObj = cacheItem.Value;
cacheItem.Value = cacheObj;
args.UpdatedCacheItem = cacheItem;
var policy = new CacheItemPolicy
{
UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate),
AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval)
};
args.UpdatedCacheItemPolicy = policy;
}
我已经弄明白了。System.Runtime.Caching.CacheExpires上有一个名为_tsPerBucket的
内部静态只读时间跨度
,在20秒时硬编码
显然,该字段用于运行并检查缓存项是否过期的内部计时器
我正在通过使用反射覆盖值并清除默认的MemoryCache实例来重置所有内容来解决这个问题。即使是一个巨大的黑客,它似乎也能工作
以下是更新的代码:
static MemoryCache MemCache;
static int RefreshInterval = 1000;
protected void Page_Load(object sender, EventArgs e)
{
if (MemCache == null)
{
const string assembly = "System.Runtime.Caching, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
var type = Type.GetType("System.Runtime.Caching.CacheExpires, " + assembly, true, true);
var field = type.GetField("_tsPerBucket", BindingFlags.Static | BindingFlags.NonPublic);
field.SetValue(null, TimeSpan.FromSeconds(1));
type = typeof(MemoryCache);
field = type.GetField("s_defaultCache", BindingFlags.Static | BindingFlags.NonPublic);
field.SetValue(null, null);
MemCache = new MemoryCache("MemCache");
}
if (!MemCache.Contains("cacheItem"))
{
var cacheObj = new object();
var policy = new CacheItemPolicy
{
UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate),
AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval)
};
var cacheItem = new CacheItem("cacheItem", cacheObj);
MemCache.Set("cacheItem", cacheItem, policy);
}
}
private void CacheEntryUpdate(CacheEntryUpdateArguments args)
{
var cacheItem = MemCache.GetCacheItem(args.Key);
var cacheObj = cacheItem.Value;
cacheItem.Value = cacheObj;
args.UpdatedCacheItem = cacheItem;
var policy = new CacheItemPolicy
{
UpdateCallback = new CacheEntryUpdateCallback(CacheEntryUpdate),
AbsoluteExpiration = DateTimeOffset.UtcNow.AddMilliseconds(RefreshInterval)
};
args.UpdatedCacheItemPolicy = policy;
}
对于MatteoSp-配置中的pollingInterval或构造函数中的NameValueCollection是不同的计时器。这是一个间隔,调用时将使用其他两个配置属性来确定内存是否处于需要使用Trim方法删除条目的级别。基于@Jared答案的更新版本。在读取修改默认MemoryCache实例时,这里创建一个新实例
class FastExpiringCache
{
public static MemoryCache Default { get; } = Create();
private static MemoryCache Create()
{
MemoryCache instance = null;
Assembly assembly = typeof(CacheItemPolicy).Assembly;
Type type = assembly.GetType("System.Runtime.Caching.CacheExpires");
if( type != null)
{
FieldInfo field = type.GetField("_tsPerBucket", BindingFlags.Static | BindingFlags.NonPublic);
if(field != null && field.FieldType == typeof(TimeSpan))
{
TimeSpan originalValue = (TimeSpan)field.GetValue(null);
field.SetValue(null, TimeSpan.FromSeconds(3));
instance = new MemoryCache("FastExpiringCache");
field.SetValue(null, originalValue); // reset to original value
}
}
return instance ?? new MemoryCache("FastExpiringCache");
}
}
您是否愿意/能够从旧的System.Runtime.Caching更改为新的?版本1.x支持NetStandard1.3和net451。如果是这样的话,那么改进后的API将支持您描述的用法,而不需要使用反射进行黑客攻击 MemoryCacheOptions对象具有属性ExpirationScanFrequency,可用于控制缓存清理的扫描频率,请参阅
请注意,不再存在基于计时器的过期(这是一个性能设计决策),因此内存压力或为缓存项调用基于Get()的方法之一现在是过期的触发器。但是,您可以使用取消令牌强制基于时间的过期,请参阅此SO答案以获取示例。为什么要将对象缓存这么短的时间?如果对象将在5-15秒内被逐出,那么使用缓存似乎没有多大价值。我发现了更奇怪的行为:如果在CacheItemPolicy上设置RemovedCallback,延迟将减少到10秒。与我之前的评论相比:不总是10秒,-有时是0,有时是20秒。但通常情况下,10秒钟的时间很难让它发挥作用,有人能提供一些背景吗?我不明白这段代码需要在哪里才能工作。谢谢@kmdsax代码应该存在于缓存所在的位置?在这个例子中,我只是把它放在一个aspx页面中。实际上,我们有一个单例缓存对象,它在初始化时运行上面的if(MemCache==null)块代码。反射代码应该在其所在的应用程序的生命周期内应用更新的计时器分辨率,因此您只需在缓存开始时运行它。轮询间隔似乎是可配置的,请参见此处:您应该使用反射更新轮询间隔而不是写入专用字段…在这里有相同的经验。例如,将轮询间隔更改为1秒不会影响项目从缓存中删除或更新的速度。我已经很久没有接触过这些内容了,但是如果这是实现我最初要求的最好方法,那么我愿意接受您的解决方案