ConcurrentDictionary:.NET 4.0中新的线程安全的哈希表

ConcurrentDictionary 是.NET 4.0中在并行和并发编程方面显著增强的基石。但是在对其进行深入研究之前,让我们来回顾一下在.NET之前版本中存在的问题。

.NET中哈希表的第一个版本是System.Collections.Hashtable。尽管它并非是线程安全的,但在理论上你可以通过简单地调用Hashtable.Synchronized来得到线程安全的封装器。不幸的是,由于这个封装器所使用的方式,它并不是真正线程安全的,

比方说,你想要检查一个键值是否存在于集合中。如果不存在,那么你就想要执行一个不会重复的操作,在那里会将结果保存。即使ContainsKey和set_Item二者都分别是线程安全的,也没有一种方式能够直接对它们进行组合。作为替代的方法,你需要采用SyncRoot上的锁,这会推翻你在前面请求同步版本的所有理由。

当.NET 2.0引入泛型和System.Collections.Generic.Dictionary的时候,微软还是没有解决这个问题。开发者需要采用自己的显式的锁。

.NET 3.5没有添加任何技术,但是它确实使得我们更易于实现在函数式编程方面增强的程序。首先,它取消了定义自定义委托的思想。从那开始,在任何设计得足够好的API上,都可以重用泛型的Action和Func的委托。另一个优势是将lambda表达式引入到VB中,并且显著提升了它在C#中的表现。结果是,使用这个API,开发者可以很容易地像这样来创建他们自己的同步封装器:

public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)

在之前的版本中,开发者需要对锁进行操作,与此不同的是,在新的ConcurrentDictionary类中的这个方法看起来很容易就可以正确使用。我们只需要简单地提供一个键值和一个委托,如果键值不存在就会执行委托。由于函数本身是线程安全的,因此一切都应该是原子级的。

好吧,就算不是。为了“避免在锁之下执行未知的代码而引发的各种问题”,valueFactory委托没有在锁中执行。因此就可能存在竞争条件,开发者需要确保valueFactory委托只执行可重复的操作。

如果你需要这项功能,那么你需要将ConcurrentDictionary类和Lazy类组合。这样做的示例代码包含在AsyncCache类中,它也作为示例被发布了。

尽管这个功能随时都会改变,但当前ConcurrentDictionary的实现已经带有不用锁的读取了。为了提升性能,开发者可以提供写线程的估计数目。这会控制着哈希表将使用多少细粒度的锁。

相关推荐