relaxed 順序(relaxed ordering)
/ relaxed -> ree-LAKST /
relaxed 順序就是「把排序拿掉」的原子操作——它保留操作的不可分割性,卻對它如何相對於「其他」記憶體操作排隊不做任何承諾。想像牆上一個被好幾個人往上撥的計數器:每一次撥動都剛好被計一次(這就是原子性),但你不能用「計數器到了 5」去推論那些人到那時還做了別的什麼。
形式上,一個 relaxed 原子操作只保證兩件事:它是原子的(在那單一位置上沒有撕裂或遺失的更新),以及對那一個位置的存取仍尊重它自身的修改順序(每個執行緒以一致的順序看到該變數的值,絕不倒退)。它「不」對其他變數建立任何 happens-before 邊、「不」建立 synchronizes-with 關係,而且編譯器與中央處理器可自由地把它相對於周遭不相依的記憶體操作重排。它是最便宜的原子——在 x86 上往往就是一個沒有任何屏障的普通載入或儲存。
它誠實而狹窄的用途:一個事件計數器或統計累計,你只在最後讀總和;一個你只設定一次的旗標,你需要的只是它「本身」可見(而非它所守護的資料),日後再搭配一道屏障;或產生唯一識別碼。它的濫用則無所不在:人們為了「跑快一點」對一個其實正守護著資料的旗標伸手用 relaxed,於是資料就毫無排序地洩漏出去。一條經驗法則——如果你用 relaxed 去發布或消費「資料」,你幾乎肯定錯了,你要的是 release/acquire。
atomic_fetch_add_explicit(&hits, 1, memory_order_relaxed); /* 可以:只是在計數 */ /* 稍後,在 join 之後: */ long total = atomic_load_explicit(&hits, memory_order_relaxed);
純計數器是 relaxed 教科書級的安全用法:沒有其他記憶體的可見性搭它的便車。
relaxed 不代表「不需要同步」或「最終一致」——它代表你自己扛起排序的全部重擔;一個走失的 relaxed 指標發布,正是在 ARM 上讀到未初始化資料的教科書級手法。