【问题标题】:Is this non-locked TryGetValue() dictionary access thread-safe?这个非锁定的 TryGetValue() 字典访问线程安全吗?
【发布时间】:2011-10-07 23:50:41
【问题描述】:
private object lockObj = new object();

private Dictionary<int, string> dict = new Dictionary<int, string>();

public string GetOrAddFromDict(int key)
{
    string value;

    // non-locked access:
    if (dict.TryGetValue(key, out value))
        return value;

    lock (this.lockObj)
    {
        if (dict.TryGetValue(key, out value))
            return value;

        string newValue = "value of " + key; // place long operation here
        dict.Add(key, newValue);

        return newValue;
    }
}

问题a:它是线程安全的吗?如果是,为什么?

问题 b:这种双 TryGetValue() 模式是如何调用的?

【问题讨论】:

  • 旁注;对于Hashtable,这种双重检查锁定模式明确安全的。这对字典是否安全尚不清楚。

标签: c# .net multithreading dictionary thread-safety


【解决方案1】:

a) 这不是线程安全的,因为底层 Dictionary 本身不是线程安全的。如果另一个线程同时调用 Add,可能会发生未定义的行为。

b) 这实际上是double-checked locking 的尝试。

我建议改用ConcurrentDictionary 类,因为它是为这种情况设计的。如果您的目标不是 .NET 4.0,另一种选择是使用 ReaderWriterLockSlim(或 ReaderWriterLock)。

【讨论】:

  • -1 ReaderWriterLockSlim 和 ReaderWriterLock 比 'lock' 贵得多,尤其是在写入时。使用这些的唯一原因是当您为每次写入执行 100 或 1000 次读取时,并且读取锁定的持续时间足以导致其他线程的大量延迟。使用这种微不足道的代码(单个 TryGetValue),“锁定”语句是正确的同步。
  • @csharptest.net 太苛刻了;他的建议是 ConcurrentDictionary,这很好 - 由于 Reed 不是 OP,我们不知道 读者/作者率。据我们所知,ReaderWriterLockSlim 实际上可能没问题。
  • @csharptest 下面的博客文章声称ReaderWriterLockSlim 的性能开销超过Monitor 大约是2 倍。根据您的评论,我预计会超过10 倍。blogs.msdn.com/b/pedram/archive/2007/10/07/…
  • @Marc Gravell,公平地说,我有点苛刻,但在 +5 的答案中得到 -1 不会损害 Reed 的代表;)建议使用 ConcurrentDictionary;但是,这个类只有 4.0,我们不知道 OP 使用的是什么版本。因此,他对其他锁的推荐可能会导致严重的性能问题。如前所述,使用这些读取器/写入器锁不仅需要读取:写入比率的阈值,而且还需要复杂(或耗时)的读取/写入过程才能受益。我只是想确保 OP 不会走上一条非常痛苦的道路。
  • @csharptest.net:我在这里提到使用 ReaderWriterLockSlim 是因为 OP 的意图显然是不锁定读取,所以我假设(这可能是错误的)访问与阅读高度平衡- 否则,这将是不必要的优化。诚然,OP 可能正在尝试在这里进行过早的优化(很有可能),但我给了他们怀疑的好处。当且仅当每次写入有很多读取时,我强烈支持在 .NET 3.5 中使用 RWLS over lock ...
【解决方案2】:

问题a:它是线程安全的吗?如果是,为什么?

它不仅不是线程安全的;如果在另一个线程重新组织哈希桶时访问它,它也会抛出 NullReferenceException。 lock 语句邪恶快,不要回避。

问题 b:这个 double-TryGetValue() 模式是如何调用的?

大多数人将其称为“错误”;)

【讨论】:

  • 它被大多数人称为“错误”让我哈哈大笑;)+1
【解决方案3】:

很遗憾,没有。

我随身携带一个具有此属性的自定义 HashMap。

缺陷在 rehash() 函数中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多