【问题标题】:Accessing .NET dictionary in a multithreaded environment在多线程环境中访问 .NET 字典
【发布时间】:2010-12-08 07:05:11
【问题描述】:

我想使用 if-lock-if 模式来检查对象是否存在于多线程环境中的字典中。因此,我正在考虑的代码如下所示:

private IDictionary<string, SomeType> m_dic = new Dictionary<string, SomeType>();

private SomeType GetSomeObject(string key)
{
  SomeType obj;
  if (!m_dic.TryGetValue(key, out obj))
  {
    lock(m_dic)
    {
      if (!m_dic.TryGetValue(key, out obj))
      {
        m_dic[key] = obj = CreateSomeObject(key);
      }
    }
  }
  return obj;
}

我的假设是即使另一个线程现在正在同一键处插入对象,TryGetValue 也不会返回部分设置的引用(这样的东西在 .NET 中不存在,对吗?),而是它将返回 null,因此我们进入受保护部分并重复检查。

我的问题是我的假设是否正确,代码是否正确?

谢谢。

编辑

让我提出一个限制。字典实际上是单例对象的字典。因此,一旦条目被占用,它就永远不会改变。就像单例的 Instance 属性一样——一旦设置,它就永远不会改变。鉴于该约束,我们可以使用 if-lock-if 模式吗?

【问题讨论】:

  • 重新编辑:不,字典的内部缓冲区仍然不是线程安全的。

标签: .net multithreading dictionary


【解决方案1】:

在 .NET 中无法获得部分设置的引用。所有读/写引用操作都是原子的。

代码看起来不错:) 但不要忘记将同步添加到其他操作,例如 INSERT 和 UPDATE。

【讨论】:

    【解决方案2】:

    为清楚起见进行了编辑:

    不,这是一个非常糟糕的想法。您可以在简单和原子(int)上玩 if-lock 游戏,但 Dictionary 是一个包含多个移动部分的类。读取和写入必须始终同步,请参阅this MSDN page 上的线程安全部分。

    【讨论】:

    • 我知道。但是我想知道如果一个线程正在询问(TryGetValue)而另一个线程正在写入,它的内部状态是否会被破坏。或者,当实际上另一个线程已经插入了一个有效对象时,返回的答案只是空。如果状态已损坏 - 那么代码是错误的。但是,如果 TryGetValue 只是返回 null,那么代码是正确的,因为它在受保护的部分内重复调用。请详细说明您的回复。
    • 我已经编辑了我的问题以澄清一些事情。 MSDN文章讲一般情况,我的情况比较特殊。
    • 如果 TryGetValue 未记录为线程安全的,则只是未定义多线程环境中的返回值。它可以是任何东西。当然,您可以查看 TryGetValue 的源代码并发现 - 巧合的是 - 在您描述的情况下它返回 null。即使在这种情况下,您也不应该依赖它(请参阅“契约式设计”),因为确切的源代码可能会在框架的未来版本中发生变化。
    • Add() 先添加 Key 然后 TryGetvalue 可能会抛出 OutOfRange 异常。许多其他事情也可能出错。
    • 所有 字典方法不是安全的。所以基本上,根本没有任何保证,并且提供的答案是完全正确的。这是一种非常糟糕的做法。
    【解决方案3】:

    来自http://www.yoda.arachsys.com/csharp/singleton.html

    锁定其他类可以访问和锁定的对象(例如类型)可能会导致性能问题甚至死锁。这是我的一般风格偏好 - 只要有可能,只锁定专门为锁定目的创建的对象,或者为特定目的(例如,等待/脉冲队列)锁定它们的文档。通常这些对象应该是它们所使用的类的私有对象。这有助于使编写线程安全的应用程序变得更加容易。

    此外,双重检查锁定几乎总是一个坏主意。因此,我会使用类似于以下的代码:

    private static readonly object m_padlock = new object();
    private IDictionary<string, SomeType> m_dic = new Dictionary<string, SomeType>();
    
    private SomeType GetSomeObject(string key)
    {
      SomeType obj;
    
      lock (m_padlock)
      {
        if (!m_dic.TryGetValue(key, out obj))
        {
          m_dic[key] = obj = CreateSomeObject(key);
        }
      }
    
      return obj;
    }
    

    【讨论】:

    • 但是 m_dic 是私有的,所以 m_padlock 的论点很弱。我同意第二部分。
    • 拥有专用锁对象不是问题。我的问题集中在 if-lock-if 模式对字典的适用性。
    • @Henk: "Dictionary" 的开发者可能在某处使用了 lock(this)。
    • Heinzi,我希望 that 被记录在案。并被 BCL 团队避开。但我想总的来说你是对的。
    【解决方案4】:

    我认为 ReaderWriterLock 比这个场景中的方法更好。此外,您有多个读者,但只有一个作者。

    http://msdn.microsoft.com/en-us/library/system.threading.readerwriterlock.aspx

    【讨论】:

    【解决方案5】:

    my comments to the correct reply。重要的部分是:如果您在示例中将 Dictionary 替换为 Hashtable,则此方法将起作用。

    【讨论】:

      猜你喜欢
      • 2021-06-07
      • 2012-02-17
      • 1970-01-01
      • 2010-12-20
      • 1970-01-01
      • 2013-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多