BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース .NET 4.0 ベータ2でCoordination Data Structuresライブラリが改善

.NET 4.0 ベータ2でCoordination Data Structuresライブラリが改善

原文(投稿日:2009/11/14)へのリンク

Coordination Data Structures (CDS)は、直接使うこともできるし、複雑な並列処理フレームワークの構成要素としても利用できるデータ構造だ。CDSはバリア機構のような先進的な同期の仕組みや、数種類のスレッドセーフなコレクション、非同期処理の結果を取得するいくつかの異なる方法を提供する。

Barrierクラスは段階的に実行される非同期処理の同期をとるポイントを作成するのに使う。Barrierクラスは、このクラスをいくつのスレッドで使うかをあらかじめ設定してから使う必要がある。各スレッドがチェックポイントに達すると、BarrierクラスのSignalAndWaitメソッドが呼ばれる。このメソッドが呼ばれると、スレッドは他のすべてのスレッドがこのメソッドを呼び終わるまで処理の実行が停止する。そして、すべてのメソッドがSignalAndWaitメソッドを呼び終わると、一斉に処理を再開する。この停止と実行再開は複数回繰り返すことができる。繰り返すごとにCurrentPhaseNumberプロパティの値が増える仕組みになっている。同期に参加するスレッドの数や、ある時点でいくつのスレッドがチェックポイントに達していないかを監視するコードを書くこともできる。また、CurrentPhaseNumberプロパティはInt64で保持されるので、ひとつのBarrierクラスで9,223,372,036,854,775,807回の停止と実行再開をサポートしている。(以前のベータ版ではInt32で制限されていたので40億回くらいが上限だった。)

BlockingCollectionクラスはproducer-consumerパターンを実装するときに使える。最も単純な使い方をすれば、このコレクションはスレッドセーフなキューとして使える。この場合、キューが空のあいだ、consumerは処理をブロックされる。キューが大きくなりすぎてしまうのを防ぐため、再大容量を設定することもできる。最大容量に達すると、consumerがキューの中のアイテムを取り出すまでproducerの処理はブロックされる。また、一度コレクションがいっぱいになると、producerはコレクションにいっぱいになったという印をつけることができる。こうすることで、さらにアイテムが追加されることを防ぎ、ブロックされているconsumerスレッドを解放できる。

BlockingCollectionをそのまま使わなくてもいい。BlockingCollectionのコレクションを使うことも可能だ。この方法の場合、producerもconsumerも、リストの中にある任意のBlockingCollectionに対してアイテムの追加や削除を行おうとする。しかし、どのコレクションに対して操作するかは意識していない。再大容量の設定と共にこの方法を使うことで、各コレクションの間である種の負荷分散の仕組みを実現できる。

ConcurrentDictionaryクラスはアトミックな追加と更新をサポートする。このサポートを実現するため、GetOrAddメソッドとAddOrUpdateメソッドは引数にデリゲートをとることがある。コレクションにキーが存在しない場合、追加を行うデリゲートが呼ばれ、存在する場合は、そのキーに対応する値が返されるか、更新を行うデリゲートが呼ばれるかのどちらかだ。

並列処理のためのリンクリストも準備されていたが、ベータ2からは落とされた。率直に言って、性能と使いやすさのバランスがうまくとれなかったのが、このリンクリストをクラスとして実装できなかったことの原因だ。Joshua Phillips氏は次のように書いている

ソフトウエアのプロなら誰だって、プライドをぐっと飲み込んで愛すべき創造物を捨て去らなければならないかも知れないときがやってくるものです。それは、突き詰めて考えてみれば、そのすてきな想像の産物にはなにか原因があって、自身の存在を正当化するほどの価値を持っていなかったということでしょう。 Beta 1では、皆さんに興奮をもたらしたConcurrentLinkedList<T>ですが、手放さなければならなくなりました。残念なことに我々は、与えられた時間の中ではこのクラスを使いやすく動作するものに仕上げることができませんでした。スレッドセーフで非常にスケーラブルなリンクリストの実装はたくさんあるようです。しかし、そのスケーラビリティは何らかの条件に基づいていたり、あるいは設計上に奇妙な注意点を生み出し、その結果、その型の実用性が著しく劣化してしまっています。このような状況のなかで、CLL<T>を除外するのはとてもつらいことです。しかし現状では、リリースできるほどの十分な成果を出せなかったということですから、同情はいりません。

また、遅延評価関数のために、新しい選択肢がふたつある。値を引き回すことができるが、必要になるまで値の評価をしないfutureを使いたければ、Lazyクラスを使えばいい。このクラスは、Valueプロパティが初めてアクセスされたとき、一回だけ呼ぶことのできる関数をラップする。futureはValueプロパティを呼ぶことで実現できるが、このラップされた関数は再び呼ぶことはできない。

ふたつ目の選択肢はLazyInitializerモジュールで見つかる。このモジュールのEnsureInitializedメソッドはデリゲートを利用して値を初期化する簡便な方法だ。この方法は対象の変数がnullのときだけ使える。対象の変数が一度だけ値が割り当てられることが保証されていれば、このデリゲードを複数のスレッドから、動機オブジェクトを使わずに呼ぶこともできる。

この記事に星をつける

おすすめ度
スタイル

BT