C++とシングルトン

新たな手法を見つけ出したわけでなく、個人的な備忘録を兼ねて書きます。

具体的には以下にフォーカス。

  • C++でシングルトンを作成する方法 / Double Checked Lockingは実装可能か?
  • Windowsで軽量なシングルトンC++クラスを生成する方法があるか?


■ C++でシングルトンを実装

スレッドセーフでない生成方法として以下がある。

  • クラス内にそのクラス自身のstatic変数を作り参照を返す
  • クラス内にそのクラス自身を指すポインタstatic変数を作り、ソース側で初期化

両者の使い分けは、作成されたシングルトンクラスを削除する必要があるかないか。前者の方法だと、一度static変数で作成されたクラス全体を削除する方法がない。

■ スレッドセーフなシングルトンを実装

上記実装は、static変数を使うのでスレッドセーフとならない。呼び出しを毎回スレッドセーフにするのでは高コストなので、クラスを指す変数をチェックしてNULL値だった時にのみ、スレッドセーフにするのがDouble Checked Locking。

以下を意識しなくてはならない。

  1. クラスが不完全な状態、いわゆるoperator new直後でクラスが不完全でない時でも、NULL値以外を返すことがある。他スレッドの為にこれを防ぐ
  2. 最初のNULL値チェックを抜ける複数のスレッドが存在する場合がある。シングルトンはたった1つだけ初期化されなくてはならない。

1はメモリバリア、2はスレッド同期によって防ぐ。Windowsなら、1はCriticalSection、2はVisual C++限定で、_ReadBarrier/_WriteBarrier関数が使える。

■ おまけ

InterlockedCompareExchangePointerを使えば、不完全なクラスが書き込まれることも読み込まれる事もないので、メモリバリア不要?要検証。


if(!s_pInstance)
{
	Singleton* temp = new (nothrow) Singleton(param);
	if(InterlockedCompareExchangePointer(const_cast<Singleton*>(PVOID*)&amp;s_pInstance), temp, NULL))
	{
		delete (nothrow) temp;
	}
}
return const_cast<Singleton*>(s_pInstance);

■参考文献

[DLC2004] Double-Checked Locking,Threads,Compiler Optimizations,and More
http://www.nwcpp.org/Downloads/2004/DCLP_notes.pdf

あのScott Mayers氏のDLCに関する論文。
正しいDouble Checked Locking実装はこれを読めば大体わかる。

[JavaSingleton] double-checked lockingとSingletonパターンhttp://www.ibm.com/developerworks/jp/java/library/j-dcl/

JavaVMによっては、DCLが働かない。

[Memologue] memologue Singleton速度比較 (1)
http://d.hatena.ne.jp/yupo5656/20041011/p1

Singleton実装パターンが複数ありました。
要検証。

Leave a Reply

2 Comments on "C++とシングルトン"

Notify of
avatar
Sort by:   newest | oldest | most voted
Egtra
Guest

「クラス内にそのクラス自身のstatic変数を作り参照を返す」は、C++0xだとスレッドセーフにならないといけないらしいです。
あとはg++だと関数内static変数の初期化は、スレッドセーフだそうで、逆にスレッドセーフにしない-fno-threadsafe-staticsというオプションがあるようです。
http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Dialect-Options.html#C_002b_002b-Dialect-Options

konuma
Guest

コメント、非常に興味深いです。
>「クラス内にそのクラス自身のstatic変数を作り参照を返す」は、C++0xだとスレッドセーフにならないといけないらしいです。
なんと・・・。C++:2003にて自分が追加したスレッドセーフ対策が単なるオーバーヘッドになったら、泣きたくなりますね。
> -fno-threadsafe-statics
g++内の実装コードを読みましたが、並列書き込みに対するtry-catch検出とグローバル変数を使ったDLC実装ですね。static自体、C++使いは余り利用しないと思いますが、g++のデフォルトで仕込まなければならないくらいMayer's Singletonが流行ってしまったのは感慨深い。

wpDiscuz