メモリ消費がもったいない、と思う人がいるかもしれない。.NETにおけるobjectは、16バイトのメモリを消費する。しかし、たった16バイトでデッドロックの危険性を減らすことができるならば、それは正しい選択といえよう。
余談になるが、知り合いのソフトハウスにクレームが来たことがある。そこで作ったソフトをマルチプロセッサのサーバで20日間程度連続実行しているとハングアップする、という問題だった。
この問題を調べ、コードを解析し、直ったことを確認する手間を想像してほしい。マルチプロセッサのマシンがなければ購入しなければならない。そして再現を行い、運よくそれで修正できたとしても、確認が必要なため、最低でも40日間程度必要になる。16バイトのメモリはこれに比べればずっと安いものだと思う。
これはわりと一般的に使われるテクニックのため、気がついた人も多いのではないだろうか。いわゆる「Double-Checked Locking」と呼ばれるテクニックだ。
元のコードはこのプロパティを参照するたびに、必ずlockを行っている。ちょっとした工夫で、lockする回数を激減させることができる。
public class A {
private static A _Value;
private static object ValueLock =
new object();
public static Value {
get {
if (_Value == null) {
lock (ValueLock) {
if (_Value == null)
_Value = new A();
}
}
Return _Value;
}
}
}
最初のチェックがnullでなければ、lockする必要はない。しかし万が一、2つのスレッドが同時にこのif文を実行してしまった場合に備え、lockの後で再度チェックする、というものだ。
いちどnewされたら、二度と開放されない場合にしか使えないが、実際のコードではほとんどの場合がそのパターンに該当し、かつ実行パス上ほとんどlockに入ることがない。このため、追加コードの割には最適化度が高い。
ただし、二重チェックロックは知っていても、それを正しくプログラムすることが容易ではないことを知っている人は、意外に少ない。実は、この最適化でバグが混入したことに気づいた方はどれくらいいるだろうか。
CPUには、メモリモデルというものが決められている。メモリに対する読み書きの最適化をCPU内部でどの程度まで行い、どの程度までを保障するか、という定義だ。
昔のCPUは、コードが書かれた順番に命令実行していた。しかし、CPUが高速化するにつれて、CPU内部での命令の並べ替えが行われるようになった。これに、マルチプロセッサとメモリアクセスが絡んでくると、話が厄介になる。
現在のCPUは、大きく2二種類に分けられ、Strong Memory ModelとWeak Memory Modelと呼ばれている。Strong Memory Modelの代表はx86であり、Weak Memory Modelの代表はIA64である。簡単に言えば、Weak Memory Modelの方が命令実行順序に対する保障が弱く、そのためCPU内部での最適化の自由度は高くなるが、その分プログラマーには負担がかかる。
Copyright © ITmedia, Inc. All Rights Reserved.