2

そのため、現時点では、ダブルチェック ロックをそのまま使用しても C++ では機能しないことがわかっています。少なくとも、移植性のある方法では機能しません。

テレイン レイ トレーサーに使用する遅延四分木に脆弱な実装があることに気付きました。そこで、メモリ使用量を 4 倍にしたり、実装されたアルゴリズムの大部分を並べ替えたりしたくないので、安全な方法で遅延初期化を引き続き使用する方法を見つけようとしました。

このトラバーサルは、 C++の 12 ページのパターンと二重チェックのロックの危険性に触発されていますが、より安価に実行しようとしています。

(pseudo code!)

struct Foo {
    bool childCreated[4];
    Mutex mutex[4];
    Foo child[4];

    void traverse (...) {
        ...
        if (!childCreated[c]) { 
            // get updated view
            #pragma flush childCreated[c]
            if (!childCreated[c]) { 
                ScopedLock sl (mutex[c]);
                if (!childCreated[c]) {
                    create (c);
                    #pragma flush childCreated[c]  
                    childCreated[c] = true;
                }
            }
        }
    }
}

#pragma flushまた、コンパイラとプロセッサがそれらの間で操作の順序を変更することが許可されないハードシーケンスポイントとしても機能すると想定されています。

どのような問題が見られますか?

編集:バージョン 2、Vlads の回答を考慮しようとしています (3 番目のフラッシュを導入):

(pseudo code!)

struct Foo {
    bool childCreated[4];
    Mutex mutex[4];
    Foo child[4];

    void traverse (...) {
        ...
        if (!childCreated[c]) { 
            // get updated view
            #pragma flush childCreated[c]
            if (!childCreated[c]) { 
                ScopedLock sl (mutex[c]);
                #pragma flush childCreated[c]
                if (!childCreated[c]) {
                    create (c);
                    #pragma flush childCreated[c]
                    childCreated[c] = true;
                }
            }
        }
    }
}

編集:バージョン 3、これはバージョン 2 とかなり同等であることがわかりました。これは、子自体を使用しているのではなく、有効性をチェックするためにプリミティブ フラグを使用しているためです。基本的には、子の作成とそのフラグへの書き込みの間のメモリ バリアに依存しています。

(pseudo code!)

struct Foo {
    bool childCreated[4];
    Mutex mutex[4];
    Foo child[4];

    void traverse (...) {
        ...
        if (!childCreated[c]) { 
            ScopedLock sl (mutex[c]);
            #pragma flush childCreated[c]
            if (!childCreated[c]) {
                create (c);
                #pragma flush childCreated[c]
                childCreated[c] = true;
            }
        }
    }
}
4

1 に答える 1

3

あなたのパターンは正しくないようです。スレッド #1 が最初の#pragma flush. 次に、制御がスレッド #2 に切り替わり、スレッド #2 が実行されて が作成cされ、制御が second の直前に戻されます#pragma flush。ここで、最初のスレッドが起動し、新たに子を作成します。

編集:申し訳ありませんが、間違っています:ロックを取得できません。

編集 2: いいえ、まだ正しいです。値はスレッド #1 でフラッシュされないためです。

于 2010-03-03T15:42:43.777 に答える