MemoryCache.AddOrGetExisting是用来做什么的?

27

MemoryCache.AddOrGetExisting的行为描述如下:

使用指定的键、值和绝对过期时间将缓存条目添加到缓存中。

它的返回结果是:

如果具有相同键的缓存条目存在,则为现有缓存条目;否则为 null。

这种语义方法的目的是什么?这有什么例子吗?


3
懒加载与使用示例 https://dev59.com/I2kv5IYBdhLWcg3wfA4P#15894928 - Dave Hillier
4
好的,我很高兴我不是唯一一个...我期望在第一次调用时得到我刚传入的值,而不是 null。 - Nicholas Piasecki
3个回答

23

有时候您只想在不存在匹配条目的情况下创建缓存项(也就是说,您不想覆盖现有值)。

AddOrGetExisting 可以让您原子性地执行此操作。如果没有 AddOrGetExisting,将无法以原子、线程安全的方式执行获取-测试-设置。例如:

 Thread 1                         Thread 2
 --------                         --------

 // check whether there's an existing entry for "foo"
 // the call returns null because there's no match
 Get("foo")

                                  // check whether there's an existing entry for "foo"
                                  // the call returns null because there's no match
                                  Get("foo")

 // set value for key "foo"
 // assumes, rightly, that there's no existing entry
 Set("foo", "first thread rulez")

                                  // set value for key "foo"
                                  // assumes, wrongly, that there's no existing entry
                                  // overwrites the value just set by thread 1
                                  Set("foo", "second thread rulez")

(另请参见Interlocked.CompareExchange方法,该方法可在变量级别上进行更复杂的等效操作,还有关于test-and-setcompare-and-swap的维基百科条目。)


1
很好的解释。但是为什么我们只使用 Add 呢?总是使用 AddOrGetExisting 不是更好吗? - Joze
4
这并非完全“不可能”,因为您可以在锁定语句内部包装Get/Set调用。 - rymdsmurf

9
LukeH的回答是正确的。因为其他回答表明该方法的语义可能有不同的解释,所以我认为值得指出AddOrGetExisting实际上不会更新现有的缓存条目。
因此,这段代码
Console.WriteLine(MemoryCache.Default.AddOrGetExisting("test", "one", new CacheItemPolicy()) ?? "(null)"); Console.WriteLine(MemoryCache.Default.AddOrGetExisting("test", "two", new CacheItemPolicy())); Console.WriteLine(MemoryCache.Default.AddOrGetExisting("test", "three", new CacheItemPolicy()));
将打印
(null) one one 还有一件事需要注意:当AddOrGetExisting发现一个现有的缓存条目时,它不会处理传递给调用的CachePolicy。如果您使用自定义更改监视器设置昂贵的资源跟踪机制,这可能会有问题。通常,在缓存条目被驱逐时,缓存系统会调用Dipose()您的ChangeMonitors。这为您提供了取消注册事件等操作的机会。然而,当AddOrGetExisting返回现有条目时,您必须自己处理。

还需注意的是:在幕后,有Add方法或调用AddOrGetExisting - springy76

4

我实际上没有使用过这个功能,但我猜想一种可能的使用情况是,如果您想无条件地使用新条目更新缓存中特定键的条目,并且您想显式处理返回的旧条目。


+1 表示“当您想要显式处理旧条目时”。 - Seph
3
这很有道理,但我想知道它是否属实……似乎没有人解释为什么第一次调用AddOrGetExisting时会返回null。 - Alex Dresko
第一次返回null是有道理的——从名称上看,它不存在,所以添加-否则返回。您还可以将其用作检查值是否已存在的方法。 - user369142
你已经拥有现有的值,你正在传递它!除了方便之外,为什么你需要在返回类型中呢?是的,这很惊人,但保留旧值确实有其有效用例。 - Ryan Leach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接