happens-before 關係(the happens-before relation)
happens-before 是整個記憶體模型中最重要的單一關係,而且儘管名字如此,它「不是」在講掛鐘時間。它是一個關於「可見性」的保證:若操作 A happens-before 操作 B,那麼 B 保證能看到 A 的效果(以及 A 先前已看到的一切)。若兩者誰都不 happens-before 另一個,它們就是「並行」的,模型對它們彼此看得到什麼不給任何承諾。
它由兩個更簡單的片段黏合而成。第一,在單一執行緒內,每個敘述在程式順序中「sequenced-before」下一個——這部分就是普通的單執行緒順序。第二,跨執行緒時,一個被配對的 acquire 讀到的 release 操作會「synchronizes-with」它。happens-before 就是這兩者的遞移閉包:把 sequenced-before 與 synchronizes-with 的邊串接起來,只要存在一條從 A 到 B 的路徑,A 就 happens-before B。所以「A sequenced-before 一個 release、該 release synchronizes-with 一個 acquire、該 acquire sequenced-before B」就給了你 A happens-before B,即使 A 與 B 在不同執行緒。
這是對「這個寫入現在應該可見了吧」這種含糊說法的嚴謹替代。DRF 保證正是用它陳述的:若每一對衝突存取(其中至少一個是寫入)都被 happens-before 排序,你的程式就是無資料競爭的,行為如同循序一致。若兩個衝突存取「沒有」被 happens-before 排序,你就有了資料競爭,那是未定義行為。學會畫出你程式碼的 happens-before 邊,正是並行推理的核心技能。
T1:a = 42; store(&flag, 1, release); T2:if (load(&flag, acquire)==1) read a; 邊:a=42 --sequenced-before--> release --synchronizes-with--> acquire --sequenced-before--> 讀 a 所以 a=42 happens-before 讀 a,而讀 a 保證能看到 42。
happens-before 是你用 sequenced-before(執行緒內)與 synchronizes-with(跨執行緒)拼接出來的鏈。
happens-before 是偏序,不是時間軸:A 可能在真實時間中較早執行,卻「不」happens-before B,此時模型對 B 關於 A 完全不給保證——「它先跑」不等於同步。