2

我正在为 C# 开发一个并发字典实现,我想知道这个GetEnumerator()实现是否实际上是(线程)安全的。

它没有做一个实际的快照,所以我想知道它是否会在以后对内部字典进行读/写时搞砸,或者它是否会暴露潜在的死锁,因为暴露的枚举IEnumerator实际上会在锁内运行。

private readonly Dictionary<TKey, TValue> internalDictionary;
private SpinLock spinLock = new SpinLock();

IEnumerator IEnumerable.GetEnumerator()
{
    IEnumerator enumerator;

    bool lockTaken = false;
    try
    {
        spinLock.TryEnter(ref lockTaken);
        enumerator = (this.internalDictionary as IEnumerable).GetEnumerator();
    }
    finally
    {
        if (lockTaken)
        {
            spinLock.Exit(false);
        }
    }

    return enumerator;
}
4

2 回答 2

2

您的方法对于并发编写者来说不是线程安全的,因为

  1. 您的枚举器没有对任何内容进行快照。它正在引用原始字典。调用ToList它或其他东西来实际快照。
  2. 并发编写者不使用您的锁,因此它们同时执行。这是不安全的。
  3. 如果锁体很大,不要使用自旋锁。
  4. 如果 TryEnter 失败怎么办?毕竟叫try 。

这是一个固定版本,去掉了所有的聪明才智:

IEnumerator IEnumerable.GetEnumerator()
{
    lock (internalDictionary) return internalDictionary.ToList();
}

并发作者也必须获得锁。

于 2013-01-31T22:25:44.847 回答
1

首先想到的是,当 .NET 附带线程安全的并发集合类(包括字典)时,为什么你会想要自己创建这样一个野兽。谷歌System.Collections.Concurrent了解更多信息。

我之前对 ConcurrentDictionary 类进行了基准测试,我向您保证,它们比您自己编写的任何东西都要快得多,即使在使用自旋锁或互锁来避免任何更重的锁定机制时也是如此。

也许可以回答您的问题,但这取决于实施(这绝不是一件好事)。我猜想任何写入尝试都会失败,因为标准集合类在迭代时无法修改;但我不会依赖任何这样的机制来保证我自己的代码安全。

于 2013-01-31T21:52:05.280 回答