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では、新しい技術は追加されなかったが、全体を通してより簡単に実装することができる関数型プログラミングに主眼が置かれた。それは、カスタムデリゲートの定義を目につかなくするアイディアから始まった。そのポイントは、再利用可能なジェネリックActionとFuncデリゲートを再利用したよいデザインのAPIが期待された。そのほかのベネフィットとして、VBへのラムダ表現の追加とC#の表現の大幅な改良である。結果として、開発者はAPIを使用して以下のように、より簡単に自身の同期ラッパーを作成することができるようになった。
public TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
開発者がロックで台無しにしてしまった前のバージョンとは違い、新しいConcurrentDirectory上のこのメソッドのロックは、誤って使うことが難しくなっている。単純に、キーとキーが存在していない場合に実行されるデリゲートが提供されている。ファンクションの間、それ自身はスレッドセーフで、すべてがアトミックであるべきである。
「ロックした状態で未知のコードが実行されることから発生する可能性がある無数の問題を避ける」ため、valueFactoryデリゲートは、ロック下では実行されない。競合条件の可能性と開発者の要求を満たすため、valueFactoryデリゲートは、繰り返し操作のもとでのみ機能する。
もしこの機能が必要な場合、、ConcurrentDictionaryクラスとLazyクラスを融合させる必要がある。この例は、サンプルとしてリリースされているAsyncCacheクラスに含まれている。
変化が激しい間、現在のConcurrentDictionaryクラスは、ロックなしで読めるように実装されている。パフォーマンスを向上させるため、開発者は予測数の書き込みスレッドを提供することができる。これは、ハッシュテーブルユーザーにきめ細かいロックを提供するだろう。
Stephen Toub氏の投稿でConcurrentDictionaryについてより詳しく学ぶことができる