ConcurrentDictionary + Lazy——实例化只会发生一次吗?

问题描述 投票:0回答:1

场景

假设我们有:

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()
最多会被调用一次。

我个人有一个印象,这是错误的。真相是什么?

c# .net-core concurrency lazy-loading concurrentdictionary
1个回答
13
投票

Instantiate
确实只会执行一次。
GetOrAdd
的文档说:

如果在不同线程上同时调用 GetOrAdd, addValueFactory可能会被多次调用,但是它的键/值对 可能不会为每次通话添加到词典中。

这意味着:即使

addValueFactory
运行多次 - 只有一个返回值实际上会添加到字典中并从
GetOrAdd
调用返回。因此,如果两个线程使用相同的键同时调用
GetOrAdd
- 创建 2 个
Lazy<Heavy>
实例,但只有 1 个实例被添加到字典中并从 both
GetOrAdd
调用返回,另一个将被丢弃(因此,即使工厂已经运行了——并不意味着这个工厂提供的值就是最终从
GetOrAdd
返回的值)。因为你在
.Value
的结果上调用
GetOrAdd
- 你总是在
Lazy<Heavy>
的单个实例上调用它,所以
Instantiate
总是最多运行一次。

© www.soinside.com 2019 - 2024. All rights reserved.