2014-12-02 85 views
14

Dekker式同步的失敗通常是通過重新排序指令來解釋的。即,如果我們寫爲什麼C++ 11的acquire_release範圍不夠用於Dekker同步?

atomic_int X; 
atomic_int Y; 
int r1, r2; 
static void t1() { 
    X.store(1, std::memory_order_relaxed) 
    r1 = Y.load(std::memory_order_relaxed); 
} 
static void t2() { 
    Y.store(1, std::memory_order_relaxed) 
    r2 = X.load(std::memory_order_relaxed); 
} 

然後負載可以與商店進行重新排序,從而導致r1==r2==0

我在等一個acquire_release圍欄,以防止這種重新排序:

static void t1() { 
    X.store(1, std::memory_order_relaxed); 
    atomic_thread_fence(std::memory_order_acq_rel); 
    r1 = Y.load(std::memory_order_relaxed); 
} 
static void t2() { 
    Y.store(1, std::memory_order_relaxed); 
    atomic_thread_fence(std::memory_order_acq_rel); 
    r2 = X.load(std::memory_order_relaxed); 
} 

負載不能柵欄上方移動,商店不能移動柵欄下方,所以壞的結果應防止。

然而,實驗表明r1==r2==0仍然會發生。有沒有基於重新排序的解釋?我的推理在哪裏存在缺陷?

回答

8

據我所知(主要來自讀取Jeff Preshings blog),一個atomic_thread_fence(std::memory_order_acq_rel)防止任何重排序除了StoreLoad,即,它仍然允許以重新排序Store與隨後Load。但是,這正是您的示例中必須防止的重新排序。

更準確地說,一個atomic_thread_fence(std::memory_order_acquire)防止任何以前Load的重新排序與任何後續Store和任何後續Load,即,它可以防止在整個圍欄LoadLoad和​​重排序。

一種atomic_thread_fence(std::memory_order_release)防止任何隨後Store重排序與任一前述Store和任一前述Load,即,它可以防止在整個圍欄​​和StoreStore重排序。

一種atomic_thread_fence(std::memory_order_acq_rel)然後阻止結合,即,它阻止LoadLoad,​​,和StoreStore,這意味着只有StoreLoad仍可能發生。

4

memory_order_acq_rel實際表現就如同在同一個地方獲取和釋放柵欄。但問題在於,它們不能阻止所有可能的重新排序,它們會阻止隨後的負載或之前的商店在圍欄周圍重新排序。因此,之前的貨物和隨之而來的商店仍然可以穿過圍欄。

在Dekker同步中,重要的是防止例如在另一個線程的存儲之前,即在圍欄之前重新排序負載。 現在,在發生此同步的地方展開循環,您會發現上一次迭代的負載可能會在當前迭代中穿過柵欄。

memory_order_seq_cst對Dekker的同步工作正常,因爲它阻止了這一點的任何重新排序。例如,使用Dekker算法和mfence進行工作竊取。

爲了更好的理解,看到香草薩特講座「Atomic<> weapons 1/2」偉大的動畫,在0:43。

相關問題