屏障作用:
-
可見性:當一條執行緒修改了一個變數的值,新值會立即被寫入主記憶體,同時其他執行緒讀取該變數時會從主記憶體中讀取最新值,而不是使用執行緒快取中的值。
-
有序性:編譯器和處理器可能會對指令進行重排以提高效能,但這種重排可能會導致其他執行緒看到不一致的狀態。變數的讀寫操作前後會插入特定的記憶體屏障,這些屏障會禁止指令重排,從而保證了操作的順序性。
屏障類別:
- LoadLoad(讀讀屏障):先執行屏障前的 讀,後執行屏障後的 讀。
- LoadStore(讀寫屏障):先執行屏障前的 讀,後執行屏障後的 寫。
- StoreLoad(寫讀屏障):先執行屏障前的 寫,後執行屏障後的 讀。
- StoreStore(寫寫屏障):先執行屏障前的 寫,後執行屏障後的 寫。
插入時機表:
屏障需求 | 第二個操作 | |||
第一個操作 | 普通讀 | 普通寫 |
volatile讀 獲取鎖 |
volatile寫 釋放鎖 |
普通讀 | LoadStore | |||
普通寫 | StoreStore | |||
volatile讀 獲取鎖 |
LoadLoad | LoadStore | LoadLoad | LoadStore |
volatile寫 釋放鎖 |
StoreLoad | StoreStore |
程式碼示例:
class X {
int a, b;
volatile int v, u;
void f() {
int i, j;
i = a; // load a
j = b; // load b
i = v; // load v
// LoadLoad - volatile讀 和 volatile讀 之間插入LoadLoad
j = u; // load u
// LoadStore - volatile讀 和 普通寫 之間插入LoadStore
a = i; // store a
b = j; // store b
// StoreStore - 普通寫 和 volatile寫 之間插入StoreStore
v = i; // store v
// StoreStore - volatile寫 和 volatile寫 之間插入StoreStore
u = j; // store u
// StoreLoad - volatile寫 和 volatile讀 之間插入StoreLoad
i = u; // load u
// LoadLoad - volatile讀 和 普通讀 之間插入LoadLoad
// LoadStore - volatile讀 和 普通寫 之間插入LoadStore
j = b; // load b
a = i; // store a
}
}