并发字典中的valueFactory

时间:2015-09-01 14:30:44

标签: c# delegates func concurrentdictionary

我正在编写一个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);
    }

我注意到,在第一个GetorAdd方法之后,即使dict中已存在密钥,也始终执行了valueFactory方法并且Class1构造函数被调用了两次。

但是,如果我将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 (与字典相同的定义)

谢谢。

1 个答案:

答案 0 :(得分:2)

当你这样做时:

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

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

相反,当你这样做时:

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 Method (TKey, Func)上的文档也说明了这一点:

  

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