残念ながら、Matt の回答には、C/C++ メモリ モデルではサポートされていないダブルチェック ロックと呼ばれるものが含まれています。(これは、Java 1.5 以降 (.NET だと思います) のメモリ モデルでサポートされています。) これは、pObj == NULL
チェックが行われてからロック (ミューテックス) が取得されるまでの間pObj
に、別のスレッドで既に割り当てられている可能性があることを意味します。 . スレッドの切り替えは、プログラムの「行」間ではなく、OS が必要とするたびに発生します (ほとんどの言語では、コンパイル後の意味はありません)。
さらに、Matt が認めているように、彼は をint
OS プリミティブではなくロックとして使用しています。そうしないでください。適切なロックには、メモリ バリア命令、場合によってはキャッシュライン フラッシュなどを使用する必要があります。ロックにはオペレーティング システムのプリミティブを使用してください。使用されるプリミティブは、オペレーティング システムが実行されている個々の CPU ライン間で変わる可能性があるため、これは特に重要です。CPU Foo で機能するものは、CPU Foo2 では機能しない可能性があります。ほとんどのオペレーティング システムは、POSIX スレッド (pthreads) をネイティブにサポートしているか、OS スレッド化パッケージのラッパーとして提供しているため、多くの場合、それらを使用した例を示すのが最善です。
オペレーティング システムが適切なプリミティブを提供し、パフォーマンスのために絶対に必要な場合は、このタイプのロック/初期化を行う代わりに、アトミックな比較およびスワップ操作を使用して、共有グローバル変数を初期化できます。基本的に、書く内容は次のようになります。
MySingleton *MySingleton::GetSingleton() {
if (pObj == NULL) {
// create a temporary instance of the singleton
MySingleton *temp = new MySingleton();
if (OSAtomicCompareAndSwapPtrBarrier(NULL, temp, &pObj) == false) {
// if the swap didn't take place, delete the temporary instance
delete temp;
}
}
return pObj;
}
これは、シングルトンの複数のインスタンス (たまたま GetSingleton() を同時に呼び出すスレッドごとに 1 つ) を安全に作成し、エクストラを破棄できる場合にのみ機能します。OSAtomicCompareAndSwapPtrBarrier
Mac OS X で提供される関数 (ほとんどのオペレーティング システムは同様のプリミティブを提供します) は、 であるかどうかをチェックしpObj
、NULL
実際に設定されtemp
ている場合にのみ設定します。これは、ハードウェアサポートを使用して、実際には文字通り一度だけスワップを実行し、それが発生したかどうかを通知します。
OS がこれら 2 つの極端な中間にある機能を提供している場合に利用できるもう 1 つの機能は、pthread_once
. これにより、基本的にすべてのロック/バリア/などを実行することにより、一度だけ実行される機能を設定できます。何回呼び出されても、何スレッドで呼び出されても。