web-dev-qa-db-ja.com

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

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

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

ただし、1)Fooのコンストラクターが複数回呼び出される可能性があるため(これは重要である場合も、そうでない場合もある)、2)instは別のスレッドに返される前に完全に構​​築されない可能性があるため、このソリューションはスレッドセーフではないことを理解しています。

1つの解決策は、メソッド全体をミューテックスでラップすることですが、実際に必要になった後、同期のオーバーヘッドに長い時間を費やしています。別の方法は次のようなものです

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であることが保証されていますか?

67
user168715

あなたのソリューションは「ダブルチェックロック」と呼ばれ、あなたが書いた方法はスレッドセーフではありません。

この Meyers/Alexandrescu paper はその理由を説明しています-しかし、その論文は広く誤解されています。 「二重チェックロックはC++では安全ではない」というミームを開始しましたが、実際の結論は、C++での二重チェックロックは安全に実装できるということです。

このペーパーには、メモリバリアを使用してDLCPを安全に実装する方法を示す擬似コードが含まれているため、実装を修正するのは難しくありません。

42
JoeG

C++ 11を使用している場合、これを行う正しい方法は次のとおりです。

Foo& getInst()
{
    static Foo inst(...);
    return inst;
}

新しい標準によれば、この問題をもう気にする必要はありません。オブジェクトの初期化は1つのスレッドによってのみ行われ、他のスレッドは完了するまで待機します。または、std :: call_onceを使用できます。 (詳細情報 ここ

83
Sat

Herb SutterがCppCon 2014のダブルチェックロックについて説明しています。

以下は、それに基づいてC++ 11で実装したコードです。

class Foo {
public:
    static Foo* Instance();
private:
    Foo() {}
    static atomic<Foo*> pinstance;
    static mutex m_;
};

atomic<Foo*> Foo::pinstance { nullptr };
std::mutex Foo::m_;

Foo* Foo::Instance() {
  if(pinstance == nullptr) {
    lock_guard<mutex> lock(m_);
    if(pinstance == nullptr) {
        pinstance = new Foo();
    }
  }
  return pinstance;
}

ここでプログラム全体を確認することもできます: http://ideone.com/olvK1

11
qqibrow

つかいます - pthread_once 。初期化関数がアトミックに1回実行されることが保証されています。

(Mac OS Xでは、スピンロックを使用します。他のプラットフォームの実装を知りません。)

8
kennytm

TTBOMK、ロックせずにこれを行う唯一の保証されたスレッドセーフな方法は、すべてのシングルトンを初期化することですbeforeスレッドを開始します。

3
sbi

別の方法は "double-checked locking" と呼ばれます。

動作するマルチスレッドメモリモデルが存在する可能性がありますが、POSIXはそれを保証しません

0
Steve Jessop

TLSはここで機能しますか? https://en.wikipedia.org/wiki/Thread-local_storage#C_and_C++

例えば、

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

ただし、次のように明示的に削除する方法も必要です。

static void deleteInst() {
   if (!inst) {
     return;
   }
   delete inst;
   inst = NULL;
}
0
Joe C

ACEシングルトンの実装では、スレッドの安全性のためにダブルチェックロックパターンが使用されます。必要に応じて参照できます。

ソースコードを見つけることができます こちら

0
baris.aydinoz