c++++內存序有六種,分別是memory_order_relaxed、memory_order_consume、memory_order_acquire、memory_order_release、memory_order_acq_rel、memory_order_seq_cst;它們分別用于控制原子操作的內存可見性和順序約束,其中relaxed僅保證原子性,consume用于數據依賴,acquire防止后續操作重排到加載前,release防止前面操作重排到存儲后,acq_rel結合兩者用于cas操作,seq_cst提供全局順序一致性;選型時根據場景選擇,統計用relaxed,同步配合使用acquire/release,cas優先acq_rel,默認用seq_cst,consume較少使用。
在c++的多線程編程中,內存序(memory_order)是控制原子操作之間內存可見性和順序約束的關鍵機制。它不是簡單的“先后執行”的問題,而是影響編譯器和CPU如何重排指令、如何同步數據的問題。
如果你寫過多線程程序,尤其是涉及無鎖結構(如lock-free隊列、原子計數器等),就一定會遇到這個問題。不同memory_order選項影響性能和正確性,選錯可能導致數據競爭或過度加鎖,拖慢程序。
memory_order有哪些?基本分類
C++11標準定義了六種內存序選項,它們分別是:
立即學習“C++免費學習筆記(深入)”;
- memory_order_relaxed:最寬松,不提供順序保證
- memory_order_consume:用于依賴鏈中的加載操作(實際應用較少)
- memory_order_acquire:確保當前加載操作之后的讀寫不會被重排到該操作之前
- memory_order_release:確保當前存儲操作之前的讀寫不會被重排到該操作之后
- memory_order_acq_rel:結合 acquire 和 release,用于原子交換或CAS操作
- memory_order_seq_cst:默認順序,完全順序一致性,代價最高但最容易理解
這些選項決定了兩個線程之間如何看到彼此的操作順序。
不同場景下怎么選?關鍵對比
1. 只需要保證原子性,不關心順序 —— memory_order_relaxed
這是最輕量級的選擇,適合只關注值的原子更新,不關心其他線程何時看到變化的情況。
比如一個簡單的計數器統計訪問次數:
std::atomic<int> count{0}; count.fetch_add(1, std::memory_order_relaxed);
注意點:
- 它不能用來同步其他變量
- 如果你用它來實現同步邏輯,很可能引入數據競爭
2. 保護后續操作的數據依賴 —— memory_order_consume
這個選項限制了依賴于當前加載值的后續操作不能被提前。例如:
std::atomic<std::string*> ptr; std::string* p = ptr.load(std::memory_order_consume); if (p) { std::cout << *p; // 依賴于ptr的值 }
適用范圍較窄,現代編譯器和硬件優化后,實際效果與acquire差別不大,使用頻率不高。
3. 控制加載后的可見性 —— memory_order_acquire
當你從一個共享變量讀取標志位,并希望確保后續代碼能看到其他線程在此之前寫入的數據時,就要用它。
比如線程B等待線程A設置完成某個標志:
// 線程A data = 42; ready.store(true, std::memory_order_release); // 線程B while (!ready.load(std::memory_order_acquire)) ; assert(data == 42); // 能看到前面的寫入
重點:
- 配合release一起使用才能形成同步關系
- 單獨使用acquire無法保證完整的順序一致性
4. 控制寫入前的順序 —— memory_order_release
這個選項確保當前寫入操作之前的所有讀寫都不會被重排到它后面。通常用于通知其他線程某個狀態已經準備好。
上面的例子中線程A用了release,就是為了防止data=42被重排到store之后。
常見用途:
- 設置條件變量標志
- 發布初始化完成的指針
5. 原子操作既要acquire又要release —— memory_order_acq_rel
適用于像compare_exchange_strong這樣的原子操作,既要看又要改。
比如實現一個簡單的自旋鎖:
bool expected = false; while (!lock.compare_exchange_weak(expected, true, std::memory_order_acq_rel)) { expected = false; }
說明:
- 它相當于“先獲取再釋放”
- 用于修改狀態并影響其他線程的行為
6. 默認選擇,簡單粗暴 —— memory_order_seq_cst
這是默認的內存序,也是最安全的選項。所有線程看到的操作順序一致。
x.store(1); // 默認就是seq_cst y.store(2);
優點:
- 簡單直觀,不容易出錯
- 在大多數情況下足夠快
缺點:
- 性能開銷最大,尤其是在多核系統上
實際建議總結
- 如果只是統計、計數,用relaxed
- 涉及線程間同步,成對使用acquire/release
- CAS類操作優先考慮acq_rel
- 一般情況下用seq_cst,除非你能明確需要更弱的順序
- consume幾乎不用,了解即可
基本上就這些,別太追求極致性能,除非你真有性能瓶頸。