簡體   English   中英

圍欄實際上如何在C ++中工作

[英]How do fences actually work in c++

我一直在努力理解柵欄實際上是如何強制代碼進行同步的。

例如說我有這個代碼

bool x = false;
std::atomic<bool> y;
std::atomic<int> z;
void write_x_then_y()
{
    x = true;
    std::atomic_thread_fence(std::memory_order_release);
    y.store(true, std::memory_order_relaxed);
}
void read_y_then_x()
{
    while (!y.load(std::memory_order_relaxed));
    std::atomic_thread_fence(std::memory_order_acquire);
    if (x)
        ++z;
}
int main()
{
    x = false;
    y = false;
    z = 0;
    std::thread a(write_x_then_y);
    std::thread b(read_y_then_x);
    a.join();
    b.join();
    assert(z.load() != 0);
}

因為發布圍牆之后是原子存儲操作,而獲取圍牆之前是原子加載,所以一切都按照預期的方式同步,並且斷言不會觸發

但是如果y不是像這樣的原子變量

bool x;
bool y;
std::atomic<int> z;
void write_x_then_y()
{
    x = true;
    std::atomic_thread_fence(std::memory_order_release);
    y = true;
}
void read_y_then_x()
{
    while (!y);
    std::atomic_thread_fence(std::memory_order_acquire);
    if (x)
        ++z;
}

然后,我聽說可能會有一場數據競賽。 但是為什么呢? 為什么必須在發布籬笆之后添加原子存儲,並在獲取籬笆之前添加原子負載,以使代碼正確同步?

如果有人可以提供一個執行場景,其中數據競爭導致斷言被觸發,我也將不勝感激。

對於第二個片段,沒有真正的數據爭用是一個問題。 如果編譯器確實從所編寫的代碼中生成機器代碼,那么此代碼片段就可以了。

但是,編譯器可以自由生成任何機器代碼,對於單線程程序 ,該代碼等同於原始代碼。

例如,編譯器可以注意到y變量在while(!y)循環內不會更改,因此它可以一次加載此變量以進行注冊,並僅在下一次迭代中使用該寄存器。 因此,如果最初y=false ,則會得到一個無限循環。

另一種可能的優化方法是刪除while(!y)循環,因為它不包含對易失性原子變量的訪問,並且不使用同步操作。 (C ++ Standard表示,任何正確的程序最終都應執行上述指定的操作之一,因此,編譯器在優化程序時可能會依賴於該事實)。

等等。

更一般而言,C ++標准指定對任何非原子變量的並發訪問會導致未定義行為 ,就像“保修已清除”一樣。 這就是為什么您應該使用原子 y變量的原因。

另一方面,變量x不必是原子的,因為由於內存限制,對它的訪問不是並發的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM