C#中类似于Java的ConcurrentHashMap的等效物是什么?

9

有什么需要翻译的吗?


我正在寻找一个通用版本。有没有开源库可用? - CSharpDevLondon
我目前还不知道有哪些,但你可以关注一下.Net平台的并行扩展。它们有一些并发集合类,但尚未实现字典。 - Michael Meadows
你能否解释一下ConcurrentHashMap是做什么的,对于那些不熟悉它的人来说? - Drew Noakes
重复应该反过来。 - Liam
4个回答

8

5
ConcurrentHashMap的基本思想是它是线程安全的,无需锁定。因此,“使用锁定思想”基本上就像说“使用Java”。内部使用一些CAS(比较和交换)操作,并保证无论线程如何运行,某些不变量始终保持真实。由于您没有互斥(唯一的原子操作是硬件中的CAS操作),因此完全避免了死锁的可能性,通常还会有一些性能提升。当然,数据结构的实现要复杂得多(这就是为什么有一个准备好的很酷,否则它将是痛苦的来实现,甚至证明它实际上可以使用更多的线程)。我希望.NET框架中也有这样的东西。

我希望在.NET中有这样的东西。 - Omu

2

我不熟悉你所说的ConcurrentHashMap。也许你可以定义一下它在C#中需要执行的功能。

如果你只想保证集合在并发访问时是线程安全的,那么最好定义自己的锁策略来控制对其的访问。

早期版本的.NET框架中看到的Hashtable.Synchronised(...)方法已被删除(以及所有ICollection.SyncRoot实现的隐藏通过在System.Collections.Generic中显式实现)。这是因为每当你在公共可访问对象上锁定时,你就打开了其他调用者以无法控制的方式锁定你的对象的可能性,从而导致死锁的可能性。

此外,“同步”集合只能保证对单个成员的调用是安全的,但许多情况涉及“事务”,包括许多成员。请考虑以下示例:

if (collection.Contains(item))
    collection.Get(item);

map对象(无论是哪种类型)可能保证ContainsGet方法都是线程安全的,但它不能保证在调用Get时仍然包含该项。因此,您应该使用以下代码:

private readonly object _mapLock = new object();

public void Method()
{
    lock (_mapLock)
    {
        if (collection.Contains(item))
            collection.Get(item);
    }
}

@Oskar建议使用ReaderWriterLock。这个类的实现非常糟糕,性能比简单的锁还要差。如果您正在使用较新版本的框架,请改用ReaderWriterLockSlim


1
仅作性能提示 - 锁定操作非常昂贵,因此您应该执行双重检查以避免不必要的锁定:if (包含...) lock(_mapLock) if (包含...) 获取... - Allon Guralnek
1
@Allon,1)锁定并不像大家说的那么昂贵,但是确实有成本。2)你怎么知道Contains是线程安全的?如果你没有在你的线程锁定集合时调用它,另一个线程可能会修改它(也许执行内部调整大小),结果可能是不可预测的。我更喜欢总是锁定,除非有关对象的文档明确说明特定成员对于多个读者和一个写入器是线程安全的(就像一些框架成员一样)。 - Drew Noakes
Drew,由于.Contains()是只读操作(不修改共享状态),最糟糕的情况就是它返回一个错误结果(例如,它不能破坏共享状态)。如果它返回一个错误的假值,则本次操作将不会执行,也不会发生任何不良后果。如果该操作返回一个错误的真值,则对象将被锁定,然后重新检查以防止这种假阳性。这是第二个检查的原因之一(另一个原因是防止竞态条件)。 - Allon Guralnek
此外,锁非常昂贵-请查看http://bit.ly/1uLKlD,该网站实际上进行了简单的基准测试。在他的多线程测试中,使用锁定的速度慢了17倍(13081毫秒对741毫秒)。我并没有在这里发明轮子,引用自http://en.wikipedia.org/wiki/Double-checked_locking -“该模式旨在通过以不安全的方式首先测试锁定标准(“锁提示”)来减少获取锁的开销;只有在成功后才会进行实际锁定。”-注意不安全一词。 - Allon Guralnek
@Allon,锁并不是那么昂贵。当然,它们确实有成本,但如果正确实现,它们不应该增加太多额外的处理时间。此外,软件是两个状态解决方案的一部分,另一个状态是硬件。更大的硬件可以消除任何锁定和并发问题,除非软件编写得很糟糕,在这种情况下,任何数量的分析器都会在早期发现它,允许为下一次迭代进行重写或在原型设计期间显示出来。至于这个例子,我的计时是100百万锁需要7294毫秒,而没有锁只需要572毫秒。 - scope_creep

-1

C#(以及.NET的其他部分)具有Hashtable...它位于System.Collections中。我不确定Java中的“Concurrent”HashMap是什么,但是当只有一个写入者和任意数量的读取者时,Hashtable应该是线程安全的...除此之外,您必须自己管理并发性。


1
如果编写者进行添加操作导致哈希表重新哈希,那么读取者的枚举器就不会保持稳定。 - Oskar
我曾经在MSDN上看到过这个,虽然我自己没有测试过。所以我希望它是真的,但我还没有证明过。 - GWLlosa
MSDN: Hashtable 可以被多个读线程和单个写线程安全地使用。 但是: 枚举集合本质上不是线程安全的过程。即使集合已经同步,其他线程仍然可以修改集合,这会导致异常。 - GWLlosa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接