5

C++11 アトミック ライブラリを使用して seqlock を作成したいと考えています。stackoverflow の seqlock に関するいくつかの質問を読みましたが、誰も助けてくれませんでした。私が使用するアルゴリズムは一般的で、どこにでもあります。それが私のコードです。

struct sequence_spinlock_t {

    void write_lock() {
        lock.lock();
        flags.fetch_add(1, memory_order_acquire); //A

    }

    void write_unlock() {
        flags.fetch_add(1, memory_order_release); //B
        lock.unlock();
    }

    void read_enter(uintptr_t *flag) {
        for (;;) {
            uintptr_t f = flags.load(memory_order_acquire); //C
            if ((f & 1) == 0) {
                *flag = f;
                break;
            }
            pause();
        }
    }

    bool_ read_leave(uintptr_t flag) {                                        

        uintptr_t f = flags.load(memory_order_relaxed); //D
        return f == flag;
    }

    spinlock_t lock;
    atomic_uintptr_t flags;
};

    //read thread
    uintptr_t flag;
    do {
        lock.read_enter(&flag);      (0)
        //read something             (1)
    } while(!lock.read_leave(flag))  (2)


    //write thread
    lock.write_lock();              (3)
    //write something               (4)
    lock.write_unlock();            (5)

B と C で memory_order タグを正しく使用していることを確認します。

AとDでは正しくないと思います。

保護されたデータの読み取りと書き込みを同時に行うことを考えてみてください。D のフラグの読み取り値が古すぎて、write_lock() によって書き込まれた最新の値を読み取っていないのではないかと心配しています。しかし、保護されたデータの最新の値を読み取ります。書き込みスレッドによって書き込まれます (x86 システムでは発生しない可能性がありますが、コードが x86 で実行されているとは想定していません)。読み取りスレッドが保護されたデータの読み取りを終了した後、フラグの読み取り値が古すぎるため、シーケンスが増加したことがわかりません。読み取りスレッドがループから抜け出し、バグが発生します。

(1)で保護されたデータの読み取り値は(4)で書き込まれ、(2)で読み取られたフラグの値は(3)で書き込まれません(前回書き込みロックを解除したときに書き込まれます)。バグがあると思う理由。

しかし、これを修正する方法が本当にわかりません。read_leavee() と write_locke() の間に「同期」関係を作ろうとしました (「read_leave() を write_locke() と同期させたい」)。しかし、ストアはありません。アクションは read_leave() で、失敗しました。

(ああ、C++ の標準仕様は難しすぎて理解できません。私が英語圏ではないことも一因です。)

4

2 に答える 2

2

seqlock が保護しているデータはアトミック タイプである必要があり、アクセスするにはアトミック ロード/ストアを使用する必要があることに注意してください。理想的ではありませんが、それが C11/C++11 が提供するものです。詳細については、Han Boehm の MSPC 論文を参照してください。

于 2013-12-11T16:58:17.797 に答える
2

read_leave で memory_order_relaxed を使用すること自体は問題ありませんが、フラグ変数をロードする前にデータ値がロードされていることを確認する必要があります。これは std::atomic_thread_fence で行うことができます。つまり、 read_leave は次のようになります

bool read_leave(uintptr_t フラグ) { atomic_thread_fence(memory_order_acquire); uintptr_t f = flag.load(memory_order_relaxed); f == フラグを返します。 }

FWIW、この変更により、コードはhttp://safari.ece.cmu.edu/MSPC2012/slides_posters/boehm-slides.pdfの例 3 のようになります。

于 2013-12-03T07:49:45.187 に答える