SlidingExpiration和MemoryCache

时间:2016-12-30 11:23:58

标签: c#

查看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 仍然是只尊重初始设定。为了让它以我想要的方式工作,我必须在重新添加它之前将其从缓存中移除!这可能会在多线程环境中导致不良竞争条件。

1 个答案:

答案 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秒。