在此ConcurrentDictionary缓存方案中是否需要锁定

M. *_*ara 3 c# concurrency multithreading

我有以下代码来缓存我在多线程应用程序中使用的并发字典中的某些类的实例.

简单地说,当我使用id参数对类进行即时化时,它首先检查字典中是否存在具有给定id的私有类的实例,如果没有,则创建私有类的实例(这需要很长时间,有时几秒),并将其添加到字典中以供将来使用.

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是,我是否需要在外部类构造函数的生成和赋值部分周围添加一个锁,以使此代码线程安全或者它是否很好?

更新:

在SLaks的建议之后,试图将ConcurrentDictionary的GetOrAdd()方法与Lazy结合使用,但遗憾的是构造函数PrivateClass仍被多次调用.有关测试代码,请参阅https://gist.github.com/3500955.

更新2:您可以在此处查看最终解决方案:https: //gist.github.com/3501446

SLa*_*aks 10

你在滥用ConcurrentDictionary.

在多线程代码中,您永远不应检查是否存在项目,如果不存在则添加它.
如果两个线程同时运行该代码,它们最终都会添加它.

一般来说,这种问题有两种解决方案.您可以将所有代码包装在一个锁中,或者您可以在一个原子操作中将其重新设计为整个代码.

ConcurrentDictionary 旨在用于这种情况.

你应该简单地打电话

 _privateClass = SomeClasses.GetOrAdd(cachedInstanceId, key => new PrivateClass());
Run Code Online (Sandbox Code Playgroud)

  • `GetOrAdd`不是原子的.请参阅http://msdn.microsoft.com/en-us/library/dd997369.aspx (2认同)
  • @Slaks是的,但是`PrivateClass`的构造函数可以被调用两次,这是我认为OP试图避免的,因为它需要很长时间。如果可以调用两次,那么“GetOrAdd”就可以了。 (2认同)
  • @MennanKara:在工厂中调用`.Value`会失败.(这与`new SomeClass()`相同)你需要在字典中存储`Lazy <T>`. (2认同)

ikh*_*ikh 7

锁定不是必需的,但您所做的不是线程安全的。ConcurrentDictionary.GetOrAdd()您应该在一个原子操作中完成所有这一切,而不是首先检查字典中是否存在某个项目,然后在必要时添加它。

否则,您将面临与常规字典相同的问题:另一个线程可能会SomeClasses在您检查是否存在之后但在插入之前添加一个条目。