为并发字典公开 GetEnumerator()

本文关键字:GetEnumerator 并发 字典 | 更新日期: 2023-09-27 17:56:48

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

为并发字典公开 GetEnumerator()

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

  1. 枚举器未创建任何快照。它引用了原始词典。调用ToList或实际快照的内容。
  2. 并发
  3. 编写器不使用您的锁,因此它们并发执行。这是不安全的。
  4. 如果锁体很大,请不要使用旋转锁。
  5. 如果尝试输入失败怎么办?毕竟这叫尝试

这是一个固定版本,删除了所有聪明之处:

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

并发写入器也必须锁定。

首先想到

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

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

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