簡體   English   中英

使用 atomic_flag 自旋鎖進行內存排序

[英]memory ordering with atomic_flag spin lock

我試圖熟悉 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 中被等效提及
以及“並發在行動”一書中。 我也在 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 並因此也獲取鎖。
我的錯誤在哪里?

你的錯誤是忘記了spin_lock是一個atomic_flag ,因此test_and_set是一個原子操作。 需要memory_order_acquirememory_order_release來防止讀取遷移到鎖定操作之前或寫入遷移到解鎖之后。 鎖本身受原子性保護,原子性始終包括可見性。

對於給定的原子變量,它有一個“修改順序”。 一旦線程 1 test_and_set 值從 0 到 1,線程 2 就不可能看到 0。

內存順序會影響所有其他內存地址的“同步”方式。 如果一個線程使用 memory-order_release 修改原子變量,則任何使用 memory_order_acquire 讀取相同變量的線程“看到”第一個線程在釋放之前所做的每個內存更改。

獲取和釋放與原子無關。 這是關於確保成功鎖定自旋鎖的每個線程“看到”之前鎖定它的每個線程的更改。

修改順序是使算法無鎖的關鍵。 線程 1 和線程 2 都試圖對同一個變量執行 test_and_set,因此根據規則,一個修改“先於”另一個修改。 因為 test_and_set “發生在”另一個“進步”之前,所以至少一個線程必須始終取得進步。 這是無鎖的定義

原子標志上的test_and_set操作被指定為具有特殊特征的讀-修改-寫操作,其中之一是:

原子讀-修改-寫操作應始終讀取在與讀-修改-寫操作關聯的寫之前寫入的最后一個值(按修改順序)。 [n3337 § 29.3/12]

這也是為什么fetch_add可以工作,而不需要簡單的加載操作來讀取修改順序中的最新值的原因。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM