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

我想知道锁定集合(例如ListHashSetDictionary而不是简单object是否有任何缺点。

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

我当前的代码如下所示:

 private static readonly Dictionary _dict = new Dictionary(); 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; } 

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

 private static readonly Dictionary _dict = new Dictionary(); 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; } 

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

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

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

虽然我会做出改变。

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

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

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

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

不可以。只要变量无法从其他任何地方访问,并且您可以保证仅在此处使用锁定,则没有缺点。 实际上, Monitor.Enter的文档(这是C#中使用的lock )就是这样做的。

但是,作为一般规则,我仍然建议使用私有对象进行锁定。 这通常更安全,如果您将此对象暴露给任何其他代码,将保护您,因为您不会打开从其他代码锁定对象的可能性。

直接回答你的问题: 没有

无论你锁定什么对象都没有区别。 .NET只关心它的引用,它的工作方式与指针完全相同。 考虑将.NET锁定为一个大的同步哈希表,其中键是对象引用,值是一个布尔表示你可以进入监视器。 如果两个线程锁定到不同的对象(a!= b),它们可以同时进入锁的监视器, 即使a.Equals(b) (这非常重要!!!)。 但如果它们锁定a和b,并且(a == b)一次只有一个将在监视器中。

只要不在范围之外访问dict,就不会对性能产生影响。 如果dict在其他地方可见,其他用户代码可能会锁定它,即使不一定需要(认为你的桌面是一个愚蠢的并锁定他在代码中找到的第一个随机对象)。

希望得到帮助。

我建议使用ICollection.SyncRoot对象来锁定而不是你自己的对象:

  private static readonly Dictionary _dict = new Dictionary(); private static readonly Object _syncRoot = ((ICollection)_dict).SyncRoot;