検索
特集

スレッドの落とし穴(4/6 ページ)

Windowsプログラマーでスレッドをいちども使ったことがない人はいないだろう。CPUのマルチコア化によって、ソースコードが上から順に実行という定説がなくなったのは久しい。このdev .NET特集では、知らぬと怖いテクニックを解説する。

PC用表示 関連情報
Share
Tweet
LINE
Hatena

 .NETは、CPUではないが.NETの基礎となるMSILにもメモリモデルがある。ECMAでのMSILの定義は、IA64も視野に入れたWeak Memory Modelになっているのだ。MSILがより強いメモリモデルを採用してしまうと、IA64で十分なパフォーマンスを出せなくなってしまうからだ。

 前述の二重チェックロックのコードは、Strong Memory Modelであれば問題ないが、Weak Memory ModelのマルチプロセッサのPCでは問題になる。先に解答を示そう。解答は、利用する.NETのバージョンによって2種類ある。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 ここで使ったThread.MemoryBarrierメソッドは、Visual Studio .NET 2003と共に出荷された.NET 1.1の新機能であるため、Visual Studio .NET 2002では使えない。.NET 1.0でも実行可能にする解答は、次のようになる。

*** 一部省略されたコンテンツがあります。PC版でご覧ください。 ***

 修正前のコードと似ているが、2行目に「volatile」キーワードを入れたことに注意してほしい。

 Weak Memory Modelにおいては、メモリへの書き込み順序が保障されない。このため元のコードでは、_Valueへ代入が行われた時点では_Valueオブジェクト自身への書き込みが終わっていない可能性がある。これを防ぐためには「volatile」キーワードを付加すればよいのだが、この方法を用いるとその変数へのすべてのアクセスにおいてCPU内部の最適化が禁止され、パフォーマンスの劣化を招く。

 .NET 1.1で追加されたMemoryBarrier()メソッドを使えば、問題の出る可能性のある場所に限って修正することができる。このため、パフォーマンスを落とさずにWeak Memory ModelのCPUでも動作するコードへと工夫ができる。

 さて、これで正しいコードとなったわけであるが、毎日書くようなコードではないだろう。そのためで、私自身でもこのコードを記憶しているわけではない。そして間違いがあってもほとんどの場合には動作してしまい、気づくのは稼働後のずっと後になってからになる。

 間違えた時の確認が非常に難しいため、ほんとうに必要な場合を除いてなるべく最初に示したコードを書くようにしている、というのを理解できただろうか。

Lock/SyncLockの代替え手段

 C#とVB.NETに組み込まれているlock/SyncLockは、非常に便利であるが、スレッド同期における問題は多岐に渡る。そのすべての用途に対する最適解とは言いがたい。

 スレッドを使う場合には、ほかの選択肢として何があるかを理解しておき、用途に合わせて正しいものを選択するようにしたい。ここではあまり知られていないが便利なクラスを2つ紹介しておこう。

Copyright © ITmedia, Inc. All Rights Reserved.

ページトップに戻る