为并发字典公开GetEnumerator()

时间:2013-01-31 20:38:06

标签: c# multithreading

我正在为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;
}

2 个答案:

答案 0 :(得分:1)

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

我之前已经对ConcurrentDictionary类进行了基准测试,我保证它们比你自己编写的任何东西都快得多,即使使用自旋锁或Interlocked来避免任何更重的锁定机制。

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

答案 1 :(得分:1)

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

  1. 您的枚举器没有快照任何内容。它是指原始字典。在其上调用ToList或实际快照。
  2. 并发编写器不使用您的锁,因此它们会同时执行。这是不安全的。
  3. 如果锁体很大,请不要使用自旋锁。
  4. 如果TryEnter失败怎么办?毕竟它叫做 try
  5. 这是一个固定版本,删除了所有聪明:

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

    并发作家也必须采取锁定。