锁定集合与syncRoot的任何缺点?

ckk*_*ght 4 .net c# multithreading locking

如果有任何不利的一面收集锁定在如我想知道List<T>,HashSet<T>Dictionary<TKey, TValue>而不是简单的object.

注意:在以下示例中,这是锁定发生的唯一位置,它不会从多个位置锁定,但可以从多个线程调用静态方法.此外,_dict永远不会在GetSomething方法之外访问.

我当前的代码如下所示:

private static readonly Dictionary<string, string> _dict = new Dictionary<string, string>();
public static string GetSomething(string key)
{
    string result;
    if (!_dict.TryGetValue(key, out result))
    {
        lock (_dict)
        {
            if (!_dict.TryGetValue(key, out result))
            {
                _dict[key] = result = CalculateSomethingExpensive(key);
            }
        }
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

另一位开发人员告诉我,锁定集合会引起问题,但我持怀疑态度.如果我这样做,我的代码会更有效吗?

private static readonly Dictionary<string, string> _dict = new Dictionary<string, string>();
private static readonly object _syncRoot = new object();
public static string GetSomething(string key)
{
    string result;
    if (!_dict.TryGetValue(key, out result))
    {
        lock (_syncRoot)
        {
            if (!_dict.TryGetValue(key, out result))
            {
                _dict[key] = result = CalculateSomethingExpensive(key);
            }
        }
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

spe*_*der 13

如果您将收藏品暴露给外界,那么,这可能是一个问题.通常的建议是锁定您独有的东西,并且永远不会被影响范围之外的代码意外锁定.这就是为什么通常最好锁定你甚至从未考虑过暴露的东西(即为此目的创建的特定锁对象).这样,当你的记忆力失败时,你永远不会得到意想不到的结果.

更直接地回答你的问题:在混合中添加另一个对象永远不会更有效率,但是在一些被认为的,但未测量的效率之前放置通常被认为是良好编码实践的东西可能是过早发生的选择.我赞成最佳实践,直到它明显造成瓶颈.

  • "锁定对象"并不总是足够好,并且可能导致死锁.需要的是每个相关锁定要求的对象,因此如果一个类有两组需要同步但彼此不同步的操作,它应该有两个这样的对象,依此类推.一个类可能需要许多这样的对象.当你看到很多这样的对象时,虽然它通常是非常细粒度锁定的优化,而不是确保正确性(并开始以不同的方式使正确性更难).这可能会导致更复杂的死锁.没有快速的规则. (4认同)
  • +1用于评估良好代码实践与未测量效率的关系. (3认同)

Jon*_*nna 6

在这种情况下,我会锁定集合; 锁的目的直接与集合有关,而与任何其他对象无关,因此在使用它作为锁对象时会有一定程度的自我注释.

虽然我会做出改变.

在文档中我没有找到任何说明TryGetValue线程安全的内容,并且如果在字典处于无效状态时调用它,则不会抛出异常(或更糟),因为它是添加新值的一半.因为它不是原子的,所以你在这里使用的双读模式(以避免获得锁定的时间)并不安全.这将不得不改为:

private static readonly Dictionary<string, string> _dict = new Dictionary<string, string>();
public static string GetSomething(string key)
{
    string result;
    lock (_dict)
    {
        if (!_dict.TryGetValue(key, out result))
        {
            _dict[key] = result = CalculateSomethingExpensive(key);
        }
    }
    return result;
}
Run Code Online (Sandbox Code Playgroud)

如果它可能涉及比不成功更多的成功读取(因此需要写入),使用ReaderWriterLockSlim将在这些读取上提供更好的并发性.

编辑:我刚刚注意到你的问题一般不是关于偏好,而是关于效率.实际上,在整个系统中使用4字节更多内存的效率差异(因为它是静态的)绝对为零.决定不是关于效率,而是因为两者具有相同的技术优点(在这种情况下)是关于你是否发现锁定集合或单独的对象更好地向另一个开发人员表达你的意图(包括你在未来).