![](/img/trans.png)
[英]Spin lock with std::atomic_flag - put the thread to sleep or not?
[英]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_acquire
和memory_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.