2012-09-06 22 views
11

スレッドセーフなダブルチェックロック(シングルトンまたは遅延初期化)を考慮して、多くの質問を読んでいます。いくつかのスレッドでは、パターンは完全に壊れていて、解決策を示唆するものがあります。C++ 11:遅延初期化の安全なダブルチェックロック。可能?

私の質問は、完全にスレッドセーフなダブルチェックされたロックパターンをC++で記述する方法ですか?もしそうなら、それはどのように見えるのですか?

これは、C++ 11の場合は、それが簡単になると思います。私が知る限りでは、C++ 11は、必要な改善をもたらす可能性のあるメモリモデルを改善しました。

私は、ダブルチェックガード変数をvolatileにすることでJavaで可能であることを知っています。 C++ 11はJavaのメモリモデルの大部分を借用していたので、それは可能かもしれないと思いますが、どうですか?

+5

C++ 11を使用できる場合は、ダブルチェックロックビジネス全体を無視し、静的ローカル変数または 'std :: call_once'を使用します。 –

+0

静的なローカルは遅延して初期化されていますか?そして 'call_once'について:これは、一度呼び出しが完全に作成されていない参照を変数に書き込まないようにするにはどうしますか? – gexicide

+3

はい、静的ローカルはスレッドセーフな方法で遅延的に初期化されます。そして 'call_once'は、被験者が一度だけ呼び出されることを保証します。実際に関数を実行する前に 'call_once'の他の呼び出しが返ってこないことを確認してください(http://en.cppreference.com/w/cpp/thread/call_onceを参照してください)。それはどのように実装されます。これらの2つのものは基本的に存在するので、のバグ double-checked lockingの実装を書いて気にしたくありません。 –

答えて

16

は単純にそうように、遅延初期化シングルトンの静的ローカル変数を使用します。

MySingleton* GetInstance() { 
    static MySingleton instance; 
    return &instance; 
} 

(C++ 11)標準は、すでに静的変数はスレッドセーフな方法で初期化され、それは可能性が高いように思われていることを保証これを少なくともあなた自身で書くものと同じくらい堅牢で実用的なものとして実装してください。

初期のthreadsafetyは、(C++ 11)規格の§6.7.4に見出すことができる:変数が初期化されている間に制御が同時に宣言を入力した場合、同時実行が条

初期化の完了を待ちます。

+11

これは非常識です。どうして、もしあなたがポインタを返すのであれば、決して** nullではないのですか? –

+1

@MatthieuM:主に、人々が基本となるオブジェクトをコピーする傾向が低くなるためです。もちろん、うまく設計されたシングルトン**はコピーコンストラクタを持っていてはいけません。私は実際には、参照による復帰とその場合の価値問題による復帰がどのように見えるのか分かりません。だから私はそれを狂気と呼ぶことはほとんどありません。 – Grizzly

+2

@Grizzly:オブジェクトをコピー可能にすべきでない場合、それを強制するのはオブジェクトに任されます。オブジェクトにグローバルにアクセスできるインスタンスが必要な場合は、それを処理する関数(あなたのもののようなもの)が必要です。これら2つのものは別々のものであり、それらを組み合わせる理由はありません。これがシングルトンパターンが愚かな理由です。 – GManNickG

3

有効なDCLP C++ 11実装を見たかったので、ここに1つあります。

グリズリーの回答では、動作は完全にスレッドセーフで、GetInstance()と同じです。

std::mutex mtx; 
std::atomic<MySingleton *> instance_p{nullptr}; 

MySingleton* GetInstance() 
{ 
    auto *p = instance_p.load(std::memory_order_acquire); 

    if (!p) 
    { 
     std::lock_guard<std::mutex> lck{mtx}; 

     p = instance_p.load(std::memory_order_relaxed); 
     if (!p) 
     { 
      p = new MySingleton; 
      instance_p.store(p, std::memory_order_release); 
     } 
    } 

    return p; 
} 
+0

実際には、 'mtx'と' instance_p'変数は、関数内の静的変数ではなく、関数の外部のグローバル変数として宣言することをお勧めします。 _every_呼び出しで 'mtx'と' instance_p'を使用して、ダブルチェックロックのポイントを無効にします(パフォーマンス上、シングルトンを静的なものとして宣言するだけかもしれません)。 – BeeOnRope

+0

@BeeOnRope有効なポイント..私はその変更を行いました。 – LWimsey

+0

あなたは基本的にはこのコードを書いてはいけないということを付け加えたいかもしれません。 Grizzlyの答えはより簡潔であり、コンパイラは将来このコードよりも速くなるようにもっと魔法を挿入するかもしれない。 – gexicide

関連する問題