シングルトンは、プログラム全体を通じて特定の 1 つの型のオブジェクトが 1 つだけ存在するようにします。二重チェックロックは、マルチスレッドアプリケーションでシングルトンを初期化するための一般的で効率的な手段です。次のコードは、その実装例を示しています。
100 class Singleton { 101 public: 102 static Singleton* instance(); 103 ... 104 private: 105 static Singleton* ptr_instance; 106 }; ... 200 Singleton* Singleton::ptr_instance = 0; ... 300 Singleton* Singleton::instance() { 301 Singleton *tmp = ptr_instance; 302 memory_barrier(); 303 if (tmp == NULL) { 304 Lock(); 305 if (ptr_instance == NULL) { 306 tmp = new Singleton; 307 memory_barrier(); 308 ptr_instance = tmp; 309 } 310 Unlock(); 311 } 312 return tmp; 313 }
ptr_instance の read (行 301) は、ロックによる保護は意図的に行なっていません。そうすることで、マルチスレッド環境でシングルトンがすでにインスタンス化されているかどうかの判定チェックを効率的にします。変数 ptr_instance について、行 301 の read と行 308 の write との間にデータ競合がありますが、プログラムは正しく機能します。しかし、データ競合を許容するプログラムを正しく記述するのは、難しい作業です。たとえば、前述の二重チェックロックのコードで、行 302 と 307 の memory_barrier() 呼び出しは、シングルトンと ptr_instance が必ず適切な順序で設定、読み取られるようにすることを目的に使用されています。そうすることで、すべてのスレッドが整合性を損なうことなくそれらを読み取ります。この memory_barrier() で実現されている機能を使用しないと、このプログラム手法は機能しません。