Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为并发字典公开GetEnumerator()_C#_Multithreading - Fatal编程技术网

C# 为并发字典公开GetEnumerator()

C# 为并发字典公开GetEnumerator(),c#,multithreading,C#,Multithreading,我正在为C#开发一个并发字典实现,我想知道这个GetEnumerator()implementation是否真的(线程)安全 它没有执行实际的快照,所以我想知道它是否会在以后读/写内部字典时出错,或者它是否会暴露潜在的死锁,因为公开的IEnumerator的枚举实际上会在锁内运行 private readonly Dictionary<TKey, TValue> internalDictionary; private SpinLock spinLock = new SpinLock(

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

它没有执行实际的快照,所以我想知道它是否会在以后读/写内部字典时出错,或者它是否会暴露潜在的死锁,因为公开的
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;
}
专用只读词典;
私有自旋锁自旋锁=新自旋锁();
IEnumerator IEnumerable.GetEnumerator()
{
IEnumerator枚举器;
bool-locktake=false;
尝试
{
自旋锁。TryEnter(参考锁定);
枚举数=(this.internalDictionary为IEnumerable).GetEnumerator();
}
最后
{
如果(已锁定)
{
spinLock.Exit(false);
}
}
返回枚举器;
}

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

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


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

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

  • 你的统计员没有抓拍任何东西。它在引用原词典。对其调用
    ToList
    ,或者调用其他东西来实际快照
  • 并发编写器不使用您的锁,因此它们会并发执行。这是不安全的
  • 如果锁体很大,不要使用旋转锁
  • 如果TryEnter失败怎么办?这就是所谓的尝试
  • 以下是一个固定版本,删除了所有聪明之处:

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

    同时写作的人也必须掌握这把锁。

    “snapsnot”我希望这是一个打字错误……是的,但他们明确建议你不要编辑一个字母的打字错误。这里没有具体问题……用粗体突出了实际问题。问题是这个实现是否是线程安全的。显而易见的答案是,ConcurrentDictionary并不是在所有的.NET平台上都受支持。至于答案,这是一个完全不公开枚举器的合理理由,而只是通过提供快照。常规键/值属性。@Claus确实是。我建议研究ConcurrentDictionary的API表面,即使您无法使用实现,因为我相信MS也考虑过同样的问题。也许你可以用C5?它是一个非常全面且线程安全的数据结构(集合、树等)库。有关详细信息,请参阅。@ClausJørgensen默认情况下C5集合也不是线程安全的。相反,我可以为您提供一个使用Interlocked的线程安全字典的实现,这是我为Fasterflect库编写的。代码可在(向下滚动至#elif DOT_NET_35行)找到。它可能没有你所需要的一切,但提供了一个体面的起点。而且,它使用联锁,因此速度相当快。是的,但这确实会返回快照;)至于3号和4号,这实际上已经被锁上了。不知道你在说什么。调用GetEnumerator不会快照字典的内容。是的,这就是我的观点。我在问题中提到,我知道它没有拍摄快照。因此,让我更恰当地回答您的问题:由于您没有拍摄快照,并且在读取枚举器时没有锁定,因此您不是线程安全的,并且正在破坏字典的内部状态。即使锁被使用,枚举数也会在第一次写入时失效(它将停止工作)。这有帮助吗?这就是我想要得到证实的,是的。