为并发字典公开 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;
}
对于并发编写器,您的方法不是线程安全的,因为
- 枚举器未创建任何快照。它引用了原始词典。调用
ToList
或实际快照的内容。
并发 - 编写器不使用您的锁,因此它们并发执行。这是不安全的。
- 如果锁体很大,请不要使用旋转锁。
- 如果尝试输入失败怎么办?毕竟这叫尝试。
这是一个固定版本,删除了所有聪明之处:
IEnumerator IEnumerable.GetEnumerator()
{
lock (internalDictionary) return internalDictionary.ToList();
}
并发写入器也必须锁定。
首先想到
的是,当 .NET 附带线程安全的并发集合类(包括字典)时,为什么你自己想要创建这样一个野兽。谷歌System.Collections.Concurrent
了解更多信息。
我之前已经对ConcurrentDictionary类进行了基准测试,我向您保证,即使使用旋转锁或互锁以避免任何较重的锁定机制,它们也比您自己编写的任何东西都要快得多。
也许可以回答您的问题,但这取决于实现(这从来都不是一件好事)。我猜任何写入尝试都会失败,因为标准集合类在迭代时无法修改;但我不会依赖任何这样的机制来保证我自己的代码安全。