記憶體模型與原子操作

acquire/release/acq_rel 順序

/ acq_rel -> AK-rel /

把 release 想成「發布」、acquire 想成「訂閱」。release 儲存就像把包裹打包好、推出門外:你在放手之前裝進去的一切,抵達時仍好好裝著。acquire 載入就像收到那個包裹:一旦拿到,你保證能看到寄出前裝在裡面的一切,而你之後做的事也不會偷跑到拆封之前。這是日常無鎖與有鎖程式中最有用的順序。

精確地說:release 操作禁止任何在程式順序中排在它「之前」的記憶體存取被重排到它「之後」;acquire 操作禁止任何排在它「之後」的存取被重排到它「之前」。它們刻意是單向屏障——release 是向下的屏障,acquire 是向上的屏障。魔法發生在它們跨執行緒、在「同一個」原子變數上配對之時:當一個 acquire 載入讀到先前某個 release 儲存所寫的值,該 release 就 synchronizes-with 該 acquire,於是建立一條 happens-before 邊,使釋放端在儲存之前做的一切,對獲取端在載入之後可見。memory_order_acq_rel 則給「單一」一個同時需要兩半的讀改寫使用(例如一個既消費又發布的 CAS)。

這個配對正是一個正確實作的互斥鎖背後的引擎:unlock() 是 release、lock() 是 acquire,這正是為何臨界區間內所做的一切,對下一個取得該鎖的執行緒都可見。它比 seq_cst 便宜,因為它不在「不相關」的原子之間強加單一全域順序——它只為真正透過彼此共享的那一個變數遞交資料的兩個執行緒排序。

data = compute(); /* 普通寫入 */ atomic_store_explicit(&ready, 1, memory_order_release); /* 另一個執行緒 */ if (atomic_load_explicit(&ready, memory_order_acquire) == 1) use(data); /* 保證能看到算好的 data */

release 發布非原子的 data,配對的 acquire 安全地消費它——最經典的交接。

單一個 release 並不會把資料推到任何地方;要等到「某個」執行緒做出配對的 acquire 並真的讀到那個被釋放的值,才會發生同步——同步需要兩端,而若讀到「不同」的值(例如陳舊的舊值)就不會建立任何 happens-before 邊。

又称
release-acquire semantics獲取-釋放語意