InfoQ

InfoQ

News

My Bookmarks

Login or Register to enable bookmarks for unlimited time.

The content has been bookmarked!

There was an error bookmarking this content! Please retry.

ConcurrentDictionary, .NET 4.0’s New Thread-Safe Hashtable

Posted by Jonathan Allen on Jan 23, 2010

Sections
Development,
Architecture & Design
Topics
Performance & Scalability ,
.NET
Tags
Concurrency ,
API-Design ,
Coordination Data Structures

The ConcurrentDictionary is a cornerstone in .NET 4.0’s greatly increased emphasis on parallel and concurrent programming. But before we delve into it, we offer a review of the problems in previous versions of .NET.

The first version of the hash table in .NET was System.Collections.Hashtable. While it wasn’t thread-safe, you could in theory get a thread-safe wrapper simply by calling Hashtable.Synchronized. Unfortunately this wrapper wasn’t actually thread-safe because of the way it was used.

Say, for example, you wanted to check if a key exists in the collection. If not, then you want to perform a non-repeatable operation, the result of which would be stored there. Even though both ContainsKey and set_Item are independently thread-safe, there is no way to directly compose them. Instead you have to take a lock on the SyncRoot, negating the whole reason you asked for a synchronized version in the first place.

When .NET 2.0 introduced generics and System.Collections.Generic.Dictionary, Microsoft punted the issue. Developers had to take explicit locks on their own or just

.NET 3.5 didn’t add anything techniques, but it did make them a whole lot easier to implement with the increased emphasis on functional programming. First of all, the idea of defining custom delegates disappeared. From that point on any well designed API would be expected to reuse the generic Action and Func delegates. Another benefit was the introduction of lambda notation to VB and a greatly improved notation in C#. As a result, developers could easily create their own synchronized wrappers with APIs such as this:

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

Unlike the earlier versions where developers had to muck about with locks, this method on the new ConcurrentDictionary looks like it hard to use incorrectly. Simply provide a key and a delegate that will be executed if the key doesn’t exist. A long as the function itself is thread-safe, everything should be atomic.

Well, no. To “avoid the myriad of problems that can arise from executing unknown code under a lock”, the valueFactory delegate isn’t executed under the locks. So there is the possibility of a race condition and developers need to ensure that the valueFactory delegate only performs repeatable operations.

If you need this functionality, you have to combine the ConcurrentDictionary class with the Lazy class. An example of this is included in the AsyncCache class, which is being shipped as a sample.

While subject to change, currently the ConcurrentDictionary class is implemented with lock-free reads. To improve performance, developers can provide an estimated number of writer threads. This will govern how many fine-grained locks the hash table uses.

You can learn more about the ConcurrentDictionary from Stephen Toub’s post.

or just "what"? by 侯 伯薇 Posted
Little mistake by Jerome St-Pierre Posted
Has C# Jumped The Shark? by John DeHope Posted
  1. Back to top

    or just "what"?

    by 侯 伯薇

    In the 4th paragraph, it seems not ends normally,
    "Developers had to take explicit locks on their own or just...",just what?

  2. Back to top

    Little mistake

    by Jerome St-Pierre

    I think it was meant to say: "As long as the function..." instead of "A long as the function...".

    Thanks for the article thought.

  3. Back to top

    Has C# Jumped The Shark?

    by John DeHope

    When I read stuff like this, I have to ask myself, is it time to start learning a language that was written to handle concurrency from day one? Things like this really feel shoehorned in.

Educational Content

Collaboration: At the Extremities of Extreme

Jason Ayers share the observations he made watching a team of developers collaborating in real time on the same code base, pushing XP, pair programming and continuous integration to their extremes.

Yesod Web Framework

Michael Snoyman presents Yesod, a web framework written in Haskell and containing a web server, templating, ORM, libraries (templating, gravatar, etc.).

Transactions without Transactions

Richard Kreuter and Kyle Banker on how to avoid classical RDBMS transactional systems by using compensation mechanisms, transactional messaging or transactional procedures.

Attila Szegedi on JVM and GC Performance Tuning at Twitter

Attila Szegedi talks about performance tuning Java and Scala programs at Twitter: how to approach GC problems, the importance of asynchronous I/O, when to use MySQL/Cassandra/Redis, and much more.

10 tips on how to prevent business value risk

One category of risk that project teams need to ensure they address is business value failure – delivering a product that fails to provide value for the business investor.

Interview: Software Systems Architecture: Working With Stakeholders Using Viewpoints and Perspectives

InfoQ spoke to the authors of Software Systems Architecture on a couple of new topics, the System Context viewpoint and Agile, which have been added to the second edition.

Beauty Is in the Eye of the Beholder

Alex Papadimoulis discusses ugly code, where it comes from, how to avoid it, and how to get rid of it.

Architecting Visa for Massive Scale and Continuous Innovation

John Davies examines Visa’s architecture and shows how enterprises have architected complex integrations incorporating Hadoop, memcached, Ruby on Rails, and others to deliver innovative solutions.