我正在使用(.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()。
但是,因此我必须实现自己的锁定以确保线程安全。
答案 0 :(得分:3)
问题在于AddOrGetExisting的第二次调用。 调用Method时,它会创建一个新的MemoryCacheEntry实例 SourceCode of .net Framework。在此条目中,它将Expiration设置为UtcNow + 1秒,使Thread.Sleep无效。 (参见构造函数的Line)
我不知道为什么它不使用现有条目的策略来确定超时。 这个line应该使用existingEntry而不是输入。也许这是一个错误?
以下是框架中产生“不良行为”的代码
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值的新条目。
答案 1 :(得分:1)
如果将线程延长到足够远,则会发生预期的行为。我假设MemoryCache的Timer机制到期了元素。
我还没有通过代码挖出足够的时间来计算默认的计时器间隔,但我怀疑它可能是10秒。使用9秒仍然失败,15秒过去了 - 对我而言。
因此,AddOrGetExisting中此错误的影响取决于您调用AddOrGetExisting的频率。如果你一直比滑动到期窗口更慢地调用它,但是比MemoryCache计时器检查到期更快,那么你将得到最坏的情况,即使它已经过期,你总是保留最旧的值。 。如果旧值和新值不相等,那么您的代码中的行为将不正确。
如果您知道有些期间您没有访问该值足够长的时间,计时器将使该项目到期,并且您可以容忍上述项目应该已到期但可能没有的可能情况那么你可能会遇到这个问题。