我有以下代码来缓存我在多线程应用程序中使用的并发字典中的某个类的实例。
简单地说,当我用 id 参数实例化类时,它首先检查字典中是否存在具有给定 id 的 privateclass 实例,如果不存在,则创建 privateclass 的实例(这需要很长时间,有时需要几个秒),并将其添加到字典中以备将来使用。
public class SomeClass
{
private static readonly ConcurrentDictionary<int, PrivateClass> SomeClasses =
new ConcurrentDictionary<int, PrivateClass>();
private readonly PrivateClass _privateClass;
public SomeClass(int cachedInstanceId)
{
if (!SomeClasses.TryGetValue(cachedInstanceId, out _privateClass))
{
_privateClass = new PrivateClass(); // This takes long time
SomeClasses.TryAdd(cachedInstanceId, _privateClass);
}
}
public int SomeCalculationResult()
{
return _privateClass.CalculateSomething();
}
private class PrivateClass
{
internal PrivateClass()
{
// this takes long time
}
internal int CalculateSomething()
{
// Calculates and returns something
}
}
}
我的问题是,我是否需要在外部类构造函数的生成和赋值部分周围添加一个锁来确保此代码线程安全,或者它是否就这样好?
更新:
在 SLaks 的 suggestion 之后, 尝试使用 GetOrAdd() ConcurrentDictionary 的方法与 Lazy 的组合, 但不幸的是 PrivateClass
的构造函数仍然调用了不止一次。参见 https://gist.github.com/3500955用于测试代码。
更新 2: 你可以在这里看到最终的解决方案: https://gist.github.com/3501446
最佳答案
你在滥用 ConcurrentDictionary
。
在多线程代码中,永远不要检查某个项目是否存在,如果不存在则添加它。
如果两个线程同时运行该代码,它们最终都会添加它。
一般来说,这种问题有两种解决方案。您可以将所有这些代码封装在一个锁中,或者您可以通过一个原子操作将其重新设计为整个代码。
ConcurrentDictionary
就是为这种场景设计的。
你应该简单地调用
_privateClass = SomeClasses.GetOrAdd(cachedInstanceId, key => new PrivateClass());
关于c# - 在此 ConcurrentDictionary 缓存场景中是否需要锁定,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12163125/