假设我们有:
var dictionary = new ConcurrentDictionary<string, Lazy<Heavy>>();
实例化
Heavy
非常消耗资源。让我们考虑一下这段代码:
return dictionary.GetOrAdd("key", key =>
{
return new Lazy<Heavy>(() =>
{
return Instantiate();
});
}).Value;
方法
Instantiate()
当然返回类型为Heavy
的实例。
对于给定的键,是否100%保证方法
Instantiate()
将被调用至多一次?
有人声称,拥有多个线程,我们只能创建多个
Lazy<Heavy>
实例,这是非常便宜的。实际方法Instantiate()
最多会被调用一次。
我个人有一个印象,这是错误的。真相是什么?
Instantiate
确实只会执行一次。 GetOrAdd
的文档说:
如果在不同线程上同时调用 GetOrAdd, addValueFactory可能会被多次调用,但是它的键/值对 可能不会为每次通话添加到词典中。
这意味着:即使
addValueFactory
运行多次 - 只有一个返回值实际上会添加到字典中并从 GetOrAdd
调用返回。因此,如果两个线程使用相同的键同时调用 GetOrAdd
- 创建 2 个 Lazy<Heavy>
实例,但只有 1 个实例被添加到字典中并从 both GetOrAdd
调用返回,另一个将被丢弃(因此,即使工厂已经运行了——并不意味着这个工厂提供的值就是最终从GetOrAdd
返回的值)。因为你在 .Value
的结果上调用 GetOrAdd
- 你总是在 Lazy<Heavy>
的单个实例上调用它,所以 Instantiate
总是最多运行一次。