12

C++ と二重チェック ロックの危険性には、著者が提案するパターンを正しく実装するための persudo コードがあります。下記参照、

Singleton* Singleton::instance () {
    Singleton* tmp = pInstance;
    ... // insert memory barrier (1)
    if (tmp == 0) {
        Lock lock;
        tmp = pInstance;
        if (tmp == 0) {
            tmp = new Singleton;
            ... // insert memory barrier (2)
            pInstance = tmp;
        }
    }
    return tmp;
}

最初のメモリバリアを return ステートメントのすぐ上に移動できるかどうか疑問に思っていますか?

編集:別の質問:リンクされた記事で、vidstigeが引用したように

技術的には、完全な双方向バリアは必要ありません。最初のバリアは、(別のスレッドによる) Singleton の構造の下方への移行を防止する必要があります。2 番目のバリアは、pInstance の初期化の上方移行を防止する必要があります。これらは「取得」および「解放」操作と呼ばれ、区別を行うハードウェア (Itainum など) 上の完全なバリアよりも優れたパフォーマンスをもたらす可能性があります。

2 番目のバリアは双方向である必要はないと言われていますが、pInstance への割り当てがそのバリ​​アの前に移動されないようにするにはどうすればよいでしょうか? 最初のバリアは上向きの移行を防ぐことができますが、別のスレッドはまだ初期化されていないメンバーを見る機会があります。

編集最初の障壁の目的をほぼ理解していると思います。sonicoderが指摘したように、if が true を返すと、分岐予測により tmp が NULL になる可能性があります。この問題を回避するには、if を読み取る前に tmp を読み取らないようにするための取得バリアが必要です。

最初のバリアは 2 番目のバリアとペアになって同期関係を実現するため、下に移動できます。

編集:この質問に興味がある人は、memory-barriers.txtを読むことを強くお勧めします。

4

2 に答える 2

4

いいえ、メモリバリアは割り当てを上向きの移行から保護するため、メモリバリアを割り当てステートメントの下に移動することはできません。リンクされた記事から:

最初のバリアは、Singleton の構造が (別のスレッドによって) 下向きに移行するのを防ぐ必要があります。2 番目のバリアは、pInstance の初期化の上方移行を防止する必要があります。

余談ですが、シングルトンのダブルチェック ロック パターンは、大きなパフォーマンス要件がある場合にのみ役立ちます。

バイナリのプロファイリングを行い、シングルトン アクセスがボトルネックになっていることを確認しましたか? そうでない場合は、二重チェックのロック パターンを気にする必要はまったくありません。

シンプルなロックを使用することをお勧めします。

于 2011-02-19T13:52:59.440 に答える