并发字典中的valueFactory

本文关键字:valueFactory 字典 并发 | 更新日期: 2023-09-27 18:10:38

我正在写一个winform程序来测试c#并发字典,下面的类:

public class Class1
{
    public int X = 10;
    public Class1(int x)
    {
        X = x;
        Debug.WriteLine("Class1 Created");
    }
}

及以下按钮代码:

  private void button1_Click(object sender, EventArgs e)
    {
        var dict = new ConcurrentDictionary<int, Class1>();
        Func<Class1> valueFactory = () => 
        {
            Debug.WriteLine("Factory Called");
            return new Class1(5);
        };
        var temp = dict.GetOrAdd(1, valueFactory());
        Debug.WriteLine(temp.X);
        temp.X = 20;
        var temp2 = dict.GetOrAdd(1, valueFactory());
        Debug.WriteLine(temp2.X);
    }

我注意到valueFactory方法总是被执行,Class1构造函数被调用两次,即使在第一个GetorAdd方法之后键已经存在于字典中。

但是,如果我将Func定义更改为

Func<int, Class1> valueFactory = (k) => 
        {
            Debug.WriteLine("Factory Called");
            return new Class1(5);
        };

并像下面这样调用GetorAdd方法:

var temp = dict.GetOrAdd(1, valueFactory);

程序以期望的方式工作,因为它在第二次调用中没有调用Class1构造函数。我怀疑这是因为我传递了一个委托valueFactory而不是函数调用valueFactory()到GetorAdd方法。我想知道是否有一个详细的解释正在发生的事情,我也不明白为什么我不能不传递valueFactory作为委托,如果我的Func定义是Func<int, Class1以外的任何东西(与字典的定义相同)

谢谢。

并发字典中的valueFactory

当你这样做的时候:

var temp = dict.GetOrAdd(1, valueFactory());
...
var temp2 = dict.GetOrAdd(1, valueFactory());

实际上是在调用dict.GetOrAdd()之前调用valueFactory 。所以它每次都被调用是正常的。当然,第二次调用valueFactory()返回的Class1的新实例最终是没有用的,但是它还是被创建了。

相反,当你这样做时:

var temp = dict.GetOrAdd(1, valueFactory);
...
var temp2 = dict.GetOrAdd(1, valueFactory);

…您实际上使用的是GetOrAdd方法的不同重载,其中您将引用传递给委托valueFactory而无需自己调用它。然后,GetOrAdd()方法根据是否在字典中找到键来决定是否需要调用valueFactory

ConcurrentDictionary最后。GetOrAdd Method (TKey, Func) doc:

使用指定的函数ConcurrentDictionary<TKey, TValue>添加一个键/值对,如果键不存在

在这种情况下,它不需要第二次调用它,因为它在第二次调用GetOrAdd()时找到了键。


然而,请注意,将valueFactory作为委托传递给GetOrAdd并不能保证它不会被调用两次。在多线程场景中尤其如此。注意ConcurrentDictionary的文档。GetOrAdd方法(TKey, Func)也表示:

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