1. ホーム
  2. c++

C++による効率的なスレッドセーフシングルトン

2023-09-10 17:11:52

質問

シングルトン・クラスの通常のパターンは次のようなものです。

static Foo &getInst()
{
  static Foo *inst = NULL;
  if(inst == NULL)
    inst = new Foo(...);
  return *inst;    
}

しかし、私の理解では、この解決策はスレッドセーフではありません。) Foo のコンストラクタは複数回呼び出される可能性があり (これは問題ではないかもしれません)、2) 別のスレッドに返される前に inst が完全に構築されない可能性があるためです。

1つの解決策は、メソッド全体にmutexを巻き付けることですが、その場合、実際にそれが必要になったずっと後に、同期のオーバーヘッドを支払うことになります。代替案は次のようなものです。

static Foo &getInst()
{
  static Foo *inst = NULL;
  if(inst == NULL)
  {
    pthread_mutex_lock(&mutex);
    if(inst == NULL)
      inst = new Foo(...);
    pthread_mutex_unlock(&mutex);
  }
  return *inst;    
}

これは正しい方法なのでしょうか、それとも何か注意すべき落とし穴があるのでしょうか?例えば、静的な初期化順序の問題が発生する可能性があります。つまり、getInst が最初に呼ばれたとき、inst は常に NULL であることが保証されますか?

どのように解決するのですか?

あなたの解決策は「二重チェックロック」と呼ばれるもので、あなたの書き方はスレッドセーフではありません。

これは Meyers/Alexandrescu 論文 がその理由を説明していますが、この論文も広く誤解されています。この論文により、「C++ では二重チェック ロックが安全でない」というミームが始まりましたが、実際の結論は、C++ における二重チェック ロックは安全に実装でき、明白でない場所にメモリ バリアを使用する必要があるだけであるというものです。

この論文には、DLCP を安全に実装するためにメモリ バリアを使用する方法を示す疑似コードが含まれています。