5

私は c++11 の新しいメモリ順序付けの概念に慣れようとしていますが、このスピン ロックの実装に出くわすまでは、実際にはかなりよく理解していると思っていました。

#include <atomic>

namespace JayZ
{
    namespace Tools
    {
        class SpinLock
        {
        private:
            std::atomic_flag spin_lock;
        public:
            inline SpinLock( void ) : atomic_flag( ATOMIC_FLAG_INIT ) {}

            inline void lock( void )
            {
                while( spin_lock.test_and_set( std::memory_order_acquire ) )
                    ;
            }

            inline void unlock( void )
            {
                lock.clear( std::memory_order_release );
            }
        };
    }
}

たとえば、http://en.cppreference.com/w/cpp/atomic/atomic_flag
や書籍「Concurrency in Action」でも同様に言及されています。ここSOのどこかでも見つけました。

しかし、なぜそれが機能するのかわかりません!
スレッド 1 が lock() を呼び出し、test_and_set() が古い値として 0 を返すと想像してください --> スレッド 1 がロックを取得しました。
しかし、その後、スレッド 2 がやってきて、同じことを試みます。ここで、「ストア同期」(release,seq_cst_acq_rel) が発生していないため、スレッド 1 の spin_lock へのストアは、relaxed 型である必要があります。
しかし、このことから、スレッド 2 の spin_lock の読み取りと同期できないことがわかります。これにより、スレッド 2 が spin_lock から値 0 を読み取り、ロックも取得できるようになります。
私の間違いはどこですか?

4

3 に答える 3

6

あなたの間違いは、それがアトミック操作であることを忘れてspin_lockいることです。andは、ロック操作の前に読み取りが移行したり、ロック解除後に書き込みが移行したりするのを防ぐために必要です。ロック自体は、常に可視性を含む原子性によって保護されています。atomic_flagtest_and_setmemory_order_acquirememory_order_release

于 2013-02-09T20:58:32.690 に答える
3

特定のアトミック変数には、「変更順序」があります。スレッド 1 が値を 0 から 1 に test_and_set すると、スレッド 2 が 0 を認識することは不可能になります。

メモリの順序は、他のすべてのメモリ アドレスが「同期」される方法に影響します。1 つのスレッドが、memory-order_release を使用してアトミック変数を変更すると、memory_order_acquire を使用して同じ変数を読み取るスレッドは、解放前に最初のスレッドが行ったすべてのメモリ変更を「認識」します。

取得と解放はアトミックに関するものではありません。スピンロックを正常にロックしたすべてのスレッドが、以前にスピンロックをロックしたすべてのスレッドの変更を「認識」するようにすることです。

変更順序は、アルゴリズムをロックフリーにするための鍵です。スレッド 1 とスレッド 2 の両方が同じ変数に対して test_and_set を実行しようとしているため、規則により、一方の変更が他方の変更の「前に発生」します。test_and_set はもう一方が「進行」する前に発生するため、少なくとも 1 つのスレッドが常に進行する必要があります。これがロックフリーの定義です

于 2013-09-04T04:02:35.137 に答える
2

test_and_setアトミック フラグに対する操作は、次のような特別な特性を持つ読み取り-変更-書き込み操作として指定されます。

アトミックな read-modify-write 操作は、read-modify-write 操作に関連付けられた書き込みの前に書き込まれた (変更順序での) 最後の値を常に読み取る必要があります。[n3337 § 29.3/12]

これがfetch_add、たとえば が機能する理由でもありますが、変更順序の最新の値を読み取るために単純なロード操作は必要ありません。

于 2013-02-09T23:23:15.017 に答える