我正在编写一个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
(与字典相同的定义)
谢谢。
答案 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
,但是每次调用时,其键/值对可能不会添加到字典中。