循序一致性(sequential consistency / seq_cst)
/ seq_cst -> SEEK-konsist /
循序一致性正是你的直覺早已偷偷假設的那個模型。想像把所有執行緒的所有記憶體操作洗在一起、排成「單一」一條時間軸,其中每個執行緒自己的操作保持它的程式順序,而每個執行緒都看到同樣的那一條時間軸。就彷彿有一位發牌員,從同一副牌、一次一張地發出每一次讀與寫。Leslie Lamport 在 1979 年定義了它,至今仍是「顯然正確」的黃金標準。
在循序一致性下,若執行緒 A 先做 x = 1 再做 y = 1,那麼任何執行緒都絕不可能在仍看到 x == 0 的同時觀察到 y == 1,因為存在一條全域順序,而 A 的兩次寫入在其中保持先後。這正是為何 seq_cst 是每個 C++ 與 C 原子的預設值:用它寫的程式幾乎可以像單執行緒程式那樣推理,只是被交錯了。純 seq_cst 下的 happens-before 關係會塌縮成那一條全序。
代價就是其中的陷阱。為了呈現單一全域順序,硬體往往必須插入一道完整的記憶體屏障——在 x86 上,一個 seq_cst 儲存通常會編譯成帶有隱含屏障的指令(或一道明確的 mfence/一條 lock 指令),它會排空儲存緩衝區並使管線停頓。在弱序晶片(ARM、POWER)上代價更高。所以 seq_cst 之所以是正確的預設,正因為它最容易寫對;而它也是該被削弱的對象——但只在已證實的熱路徑上、刻意且伴隨量測地去做。
x、y 是原子整數,初值皆 0。 T1:x=1; r1=y; T2:y=1; r2=x; 在 seq_cst 下,r1==0 且 r2==0 這個結果是「不可能」的;在 relaxed 下卻被允許。
Dekker 風格的試金石測試:seq_cst 禁止兩個讀取都看到陳舊的零;較弱的順序則不禁止。
循序一致性是最強、最直覺的模型,但它既不免費、也不是唯一正確的選擇;反過來說,它仍救不了資料競爭——即使是 seq_cst 也只適用於原子存取,普通變數上的競爭依然是未定義行為。