查看MemoryCache的文档我希望如果在Expiration期间访问了某个对象,则会刷新该期间。说实话,我认为我从名称' Sliding'中推断出来。什么都有。
然而,它出现在这个测试中
[Test]
public void SlidingExpiryNotRefreshedOnTouch()
{
var memoryCache = new MemoryCache("donkey")
{
{
"1",
"jane",
new CacheItemPolicy {SlidingExpiration = TimeSpan.FromSeconds(1) }
}
};
var enumerable = Enumerable.Repeat("1", 100)
.TakeWhile((id, index) =>
{
Thread.Sleep(100);
return memoryCache.Get(id) != null; // i.e. it still exists
})
.Select((id, index) => (index+2)*100.0/1000); // return the elapsed time
var expires = enumerable.Last(); // gets the last existing entry
expires.Should().BeGreaterThan(1.0);
}
它失败并显示一旦TimeSpan完成后对象被弹出的行为,无论该对象是否已被访问。 Linq查询在 enumerable.Last(); 语句中执行,此时只有在缓存未过期时才会执行。一旦停止,列表中的最后一项将指示项目在缓存中存在多长时间。
For Clarity 这个问题与MemoryCache的行为有关。不是linq查询。
这是否是其他任何人的期望(即每次触摸时过期不会滑动)? 是否有一种模式可以延长被触摸的对象的生命周期?
更新我发现即使我在缓存周围写了一个包装器,每次检索它时都会将对象重新添加回缓存,而另一个 SlidingExpiration 仍然是只尊重初始设定。为了让它以我想要的方式工作,我必须在重新添加它之前将其从缓存中移除!这可能会在多线程环境中导致不良竞争条件。
答案 0 :(得分:35)
... new CacheItemPolicy {SlidingExpiration = TimeSpan.FromSeconds(1) }
这在MSDN中没有充分记录。你有点不走运,1秒还不够。通过头发,使用2秒钟,你会发现它的效果就像你希望的那样。使用FromMilliseconds()修补更多内容,你会发现~1.2秒是这个程序中最开心的。
解释这个问题相当复杂,我不得不谈谈MemoryCache如何避免每次访问缓存时都必须更新滑动计时器。正如您可能想象的那样,这是相对昂贵的。让我们走一条捷径,带你到相关的Reference Source code。小到足以粘贴在这里:
internal void UpdateSlidingExp(DateTime utcNow, CacheExpires expires) {
if (_slidingExp > TimeSpan.Zero) {
DateTime utcNewExpires = utcNow + _slidingExp;
if (utcNewExpires - _utcAbsExp >= CacheExpires.MIN_UPDATE_DELTA || utcNewExpires < _utcAbsExp) {
expires.UtcUpdate(this, utcNewExpires);
}
}
}
CacheExpires.MIN_UPDATE_DELTA是关键,它阻止调用UtcUpdate()。换句话说,至少MIN_UPDATE_DELTA值的时间必须通过才会更新滑动计时器。 CacheExpired类没有被Reference Source索引,暗示他们对它的工作方式并不完全满意:)但是一个体面的反编译器可以告诉你:
static CacheExpires()
{
MIN_UPDATE_DELTA = new TimeSpan(0, 0, 1);
MIN_FLUSH_INTERVAL = new TimeSpan(0, 0, 1);
// etc...
}
换句话说,硬编码为1秒。现在无法改变它,这非常难看。此测试程序中的SlidingExpiration值需要大约1.2秒,因为Thread.Sleep(100)实际上不会休眠100毫秒,需要更多时间。换句话说,它将是第11个Get()调用,它使滑动计时器在此测试程序中滑动。你没有那么远。
嗯,这个应该有待记录,但我猜这可能会发生变化。目前,您需要假设实际滑动到期时间应至少为2秒。