我有以下代碼:
#include <atomic>
int main () {
std::atomic<uint32_t> value(0);
value.fetch_add(1, std::memory_order::relaxed);
static_assert(std::atomic<uint32_t>::is_always_lock_free);
return 0;
}
它編譯,所以這意味著std::atomic<uint32_t>::is_always_lock_free是真的。
然后,匯編代碼與 gcc 10 和-std=c 20 -O3 -mtune=skylake-avx512 -march=skylake-avx512:
0000000000401050 <main>:
401050: c7 44 24 fc 00 00 00 mov DWORD PTR [rsp-0x4],0x0
401057: 00
401058: f0 ff 44 24 fc lock inc DWORD PTR [rsp-0x4]
40105d: 31 c0 xor eax,eax
40105f: c3 ret
許多帖子指出讀-修改-寫操作(fetch_add()這里)不能是沒有鎖的原子操作。
我的問題是std::atomic::is_always_lock_free存在的true真正含義是什么。
此頁面說明Equals true if this atomic type is always lock-free and false if it is never or sometimes lock-free.
那么“這種原子型別總是無鎖的”是什么意思?
uj5u.com熱心網友回復:
這里的“Lock”是指“互斥鎖”的意思,并不是特指x86指令前綴lock.
實作std::atomic<T>任意型別的一種簡單而通用的方法T是作為一個包含一個T成員和 a 的類std::mutex,它在物件上的每個操作(加載、存盤、交換、fetch_add 等)周圍被鎖定和解鎖。這些操作然后可以以任何舊方式完成,并且不需要使用原子機器指令,因為鎖保護它們。這個實作不是無鎖的。
這種實作的一個缺點,除了一般速度慢之外,是如果兩個執行緒嘗試同時操作物件,其中一個將不得不等待鎖,這實際上可能會阻塞并導致它被調度出來一段時間。或者,如果一個執行緒在持有鎖時被調度出去,那么每個其他想要對該物件進行操作的執行緒都必須等待第一個執行緒被調度回來并首先完成其作業。
因此,如果機器支持真正的原子操作是可取的T:其他執行緒不能干擾的單個指令或序列,并且如果被中斷(或者可能根本不能被中斷)不會阻塞其他執行緒。如果對于某種型別T,庫已經能夠專門std::atomic<T>用于這樣的實作,那么這就是我們所說的無鎖的意思。(這只是在 x86 上令人困惑,因為用于此類實作的原子指令被命名為lock。在其他體系結構上,它們可能被稱為其他名稱,例如 ARM64 的ldxr/stxr專有加載/存盤指令。)
C 標準允許型別“有時是無鎖的”:也許在編譯時不知道是否std::atomic<T>是無鎖的,因為它取決于將在運行時檢測到的特殊機器功能。甚至有可能某些型別的物件std::atomic<T>是無鎖的,而另一些則不是。這就是為什么atomic_is_lock_free是函式而不是常數的原因。它檢查此特定物件在此特定日期是否無鎖。
但是,對于某些實作,在編譯時可以保證某些型別始終是無鎖的。這is_always_lock_free就是用來表示的,并注意它是一個constexpr bool而不是一個函式。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/364338.html
