5

我正在使用(.NET 4.5)MemoryCache,结合SlidingExpiration。

我注意到方法 .AddOrGetExisting() 似乎并没有牢记过期,而 .Get() 确实。

单元测试:

    [TestMethod]
    public void NonWorking()
    {
        var memCache = new MemoryCache("somekey");
        var cachePolicy = new CacheItemPolicy() { SlidingExpiration = TimeSpan.FromSeconds(1) };

        var cacheEntry = memCache.AddOrGetExisting("key1", "foo", cachePolicy);
        Assert.AreEqual(null, cacheEntry); // OK: AddOrGetExisting returns null, because it wasn't existing yet
        Thread.Sleep(1100);

        // Expecting null, since the existing item for key1 has expired by now.
        // It is, however, still "foo".
        Assert.AreEqual(null, memCache.AddOrGetExisting("key1", "bar", cachePolicy));

        // FYI: afterwards, memCache.Get("key1") still equals "foo"
    }

    [TestMethod]
    public void Working()
    {
        var memCache = new MemoryCache("somekey");
        var cachePolicy = new CacheItemPolicy() { SlidingExpiration = TimeSpan.FromSeconds(1) };

        var cacheEntry = memCache.AddOrGetExisting("key1", "foo", cachePolicy);
        Assert.AreEqual(null, cacheEntry); // OK: AddOrGetExisting returns null, because it wasn't existing yet
        Thread.Sleep(1100);
        Assert.AreEqual(null, memCache.Get("key1"));
    }

问题:

这是.AddOrGetExisting()的预期行为吗?

我可以回退到 .Get(),然后,如果为 null,则使用 .Add()。
但是,因此我必须实现自己的锁定以确保线程安全。

4

2 回答 2

3

问题在于 AddOrGetExisting 的第二次调用。调用该方法时,它会创建一个新的 .net Framework的 MemoryCacheEntry SourceCode 实例 。在此条目中,它将 Expiration 设置为 UtcNow + 1 秒,使您的 Thread.Sleep 无用。(参见构造函数的这一行)

我不知道为什么它不使用现有条目的策略来确定超时。我猜这一应该使用existingEntry 而不是entry。也许这是一个错误

这是框架中产生“不当行为”的代码

existingEntry = _entries[key] as MemoryCacheEntry;
// has it expired?
if (existingEntry != null && /* THERE => */ entry.UtcAbsExp <= DateTime.UtcNow) {
    toBeReleasedEntry = existingEntry;
    toBeReleasedEntry.State = EntryState.RemovingFromCache;
    existingEntry = null;
}

existingEntry 是应该过期的“旧条目”,entry 是具有未过期 UtcAbsExp 值的新条目。

于 2014-08-04T13:31:57.560 回答
1

如果将线程睡眠时间延长得足够远,就会发生预期的行为。我假设 MemoryCache 的 Timer 机制使元素过期。

我还没有深入研究代码来确定默认计时器间隔是多少,但我怀疑它可能是 10 秒。使用 9 秒仍然失败,15 秒过去了 - 对我来说。

因此,AddOrGetExisting 中此错误的影响取决于您调用 AddOrGetExisting 的频率。如果您调用它的速度始终比滑动到期窗口慢,但比 MemoryCache 计时器检查到期的速度快,那么您将得到最坏的情况,即您始终保留最旧的值,即使它已过期。如果旧值和新值不相等,那么您的代码中的行为将不正确。

如果您知道在某些时间段内您无法访问该值的时间足够长以至于计时器将使该项目过期,并且您可以容忍项目应该过期但没有过期的可能情况,那么您可能可以忍受这个问题。

于 2015-04-29T03:01:44.493 回答