BT

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

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース より優れたスレッドセーフなコレクションの構築

より優れたスレッドセーフなコレクションの構築

スレッドセーフなコレクションには大抵いくつかの根本的な問題がある。個々の操作がスレッドセーフである一方、大抵の場合それらの操作は結合可能ではない。スタックの先頭にある要素をポップする前に要素数を調べる、といったような一般的な操作は本質的には危険である。例えば .NET 4 の Coordination Data Structures (参考記事)などのように振る舞いの結合を図る API は存在するものの、それは TryDequeue のような不格好なメソッドにつながる。

.NET 1 のコレクションではまた別の試みが見られた。内部でロックを行う代わりに、SyncRoot プロパティを通じてそれを外部に公開したのだ。SyncRoot は同期オブジェクトのデフォルトの名称であり続けるだろうが、SyncRoot/Wrapper デザインパターンは .NET 2 で取り下げられた(リンク)
 

それではどうやって本当に有効な結合可能な API を作成するのか?Jared Parsons氏は API を直接外部に公開しない方法を提案している。その代わりにオブジェクトをロックしている間のみ利用可能な一時的なオブジェクトを作成し、それを通じて全てのメソッドを公開するのだ。この一時的なオブジェクトはコレクションの "鍵" であり、鍵の所有者のみがその中身を取得できる。

以下は Jared Parsons氏によるスレッドセーフなキュー(リンク)の例である。

static void Example1(ThreadSafeQueue queue) {
using (var locked = queue.Lock()) {
if (locked.Count > 0) {
var first = locked.Dequeue();
}
}
}

locked という名前のオブジェクトはそれ自体はスレッドセーフではなく、開発者は正しいことを行いまた "using" ブロック内でのみそれを使用する必要がある。しかしこの単純なルールに従う限り、ブロック内での全ての操作は安全である。Jared氏はこのことついて更に詳しく説明している。

大抵のスレッドセーフの設計と同様、このコードには誤った使われ方をされる可能性がある。

  1. ILockedQueue のインスタンス破棄後の使用。これは既にタブー視されていることではあるものの、この問題を緩和するのに現行のユーザのナレッジを当てにできるだろう。さらに、例えば FxCop のような静的解析ツールはこれをエラーとして通知することができる。またこれはほんのもう少し厳格化すれば未然に回避する事もできる。単に破棄フラグを追加して全てのメソッドの入り口でそれをチェックすればよい。
  2. ユーザが Lock の呼び出しと呼び出しの間に Count 等の値を保持してそれを使用することによってリストの状態に関する誤った仮定を行う可能性がある。
  3. もしユーザが ILockedQueue のインスタンスを破棄できなかった場合は永久にロックされたままになる。幸いにも IDisposable を実装しているため FxCop はこれもエラーとして通知できる。とはいえ絶対に確実な手段ではないが。
  4. ユーザに対して「ILockedQueue はごく短期間でのみ使用してください」と明確に示唆するものが何もない。IDisposable からある程度はこの意図が伝わるが、明らかに完全ではない。
  5. 実際の ILockedQueue の実装はスレッドセーフではない。ユーザがスレッド間で IDisposable のインスタンスの受け渡しを行わないのが理想的だが、考慮すべき事ではある。

 

原文はこちらです:http://www.infoq.com/news/2009/02/Threaded-Collections

この記事に星をつける

おすすめ度
スタイル

BT