addorgeexististing不考虑过期
本文关键字:过期 不考虑 addorgeexististing | 更新日期: 2023-09-27 18:16:21
我使用(。. 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()。
但是,因此我必须实现我自己的锁来确保线程安全。
问题在于AddOrGetExisting的第二次调用。当调用该方法时,它会创建一个MemoryCacheEntry的新实例.net框架的源代码。在这个条目中,它将过期时间设置为UtcNow + 1秒,创建线程。睡眠无用。(参见构造函数的这一行)
我不知道为什么它不使用现有条目的策略来确定超时。我猜这一行应该使用existingEntry而不是entry。也许这是一个bug?
下面是框架中产生"错误行为"的代码
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是应该过期的"旧条目",条目是具有未过期UtcAbsExp值的新条目。
如果您将线程睡眠扩展到足够远,则会发生预期的行为。我假设MemoryCache的Timer机制过期的元素。
我还没有深入研究代码来确定默认计时器间隔是什么,但我怀疑它可能是10秒。使用9秒仍然失败,15秒过去了——对我来说。
所以这个bug在AddOrGetExisting中的影响取决于你调用AddOrGetExisting的频率。如果您调用它的速度始终比滑动过期窗口慢,但比MemoryCache计时器检查过期的速度快,那么您将遇到最坏的情况,即始终保留最老的值,即使它已经过期。如果旧值和新值不相等,那么你的代码中就会出现不正确的行为。
如果你知道会有一段时间你没有访问这个值,计时器会使项目过期,你可以容忍上面的情况,项目应该过期,但没有,那么你可能会接受这个问题。