簡體   English   中英

比較是原子操作嗎?

[英]Is a comparison an atomic operation?

以下比較是原子動作嗎? 即,它可以簡化為單個CPU指令嗎?

char flag = 2;

for(;;)
{
    if (!flag) // <-- this
        break;

    // sleep
}

這是我正在做的事情:

int main()
{
    sf::Mutex Mutex;
    char flag = 2;

    coordinatorFunction(flag);

    for(;;)
    {
        if (!flag)
            break;

        // sleep
    }
}

void workerFunction(void* a)
{
    char* p = static_cast<char*>(a);

    // work

    GlobalMutex.Lock();
    --*p;
    GlobalMutex.Unlock();
}

void coordinatorFunction(char& refFlag)
{
    sf::Thread worker1(&workerFunction, &refFlag);
    sf::Thread worker2(&workerFunction, &refFlag);

    worker1.Launch();
    worker2.Launch();
}

這是錯誤的方法。

你的主線程盡可能快地燒掉CPU周期,除了等待flag達到零之外什么都不做。 除了最后一次測試外,每次嘗試都會失敗。 而不是以這種方式執行它,使用您的線程對象最有可能必須使“主要線程暫停”的“連接”工具,直到所有工作人員完成。

這樣, 並非巧合的是 ,你不會關心測試是否是原子的,因為你根本不需要它。

C ++操作都不能保證是原子操作。

在C ++中,沒有任何東西可以保證是原子的。

不,據我所知,C ++不保證什么是原子,什么不是 - 這是特定於平台的。 即使您的平台保證可以原子方式進行比較,也不能保證您的C ++編譯器會選擇該指令。

但是,在實踐中,簡單值類型(如char,int,float等)的比較可能是原子的。 但是,無論是在編譯器級別還是在處理器級別,您仍然需要了解可能的指令重新排序。 在這種情況下,這可能無關緊要,但在一般情況下,它可以並且確實如此。 您還需要注意,即使比較是比較然后分支也不是原子的 - 所以如果您嘗試使用標志來管理該訪問,則2個線程都可以輸入相同的代碼塊。

如果你想要適當的保證,Windows上有各種互鎖功能 ,而gcc上有原子內置功能。

不,C ++不保證任何操作都是原子的。 你的問題中的代碼很可能被編譯成從內存到寄存器的加載,這可能本身需要多個指令,然后是測試。

比較不是原子的,因為它需要幾個機器語言指令來執行它(從內存加載到寄存器等)。由於內存模型的靈活性和緩存,執行測試的線程可能無法“看到”另一個線程的結果馬上。

如果變量標記為volatile,則可能安全地進行簡單測試,但這將是特定於平台的。 正如其他人所說,C ++本身並不能保證這一點。

比較涉及讀取兩個數據以及執行實際比較。 數據可以在讀取和比較指令之間變化 ,因此它不是原子的。

但是,因為您正在比較相等性,所以_InterlockedCompareExchange (對於x86中的lock cmp xchg指令)可能會執行您所需的操作,盡管它將涉及替換數據。

你應該問的問題是“是 - 原子”嗎? 多數民眾贊成在這里。 當flag達到0時你想做點什么。

你不關心這種情況:

1. Main thread reads flag, and it is 1.
2. Worker changes flag with --
3. Main thread doesn't see that flag is actually 0.

因為在1 ns內,主線程循環並再次嘗試。

你關心的是 - 不是原子的,同時改變它的兩個線程會跳過減量:

1. Thread A reads flag, flag is 2
2. Thread B reads flag, flag is 2
3. Thread A decrements its copy of flag, 2, and writes to flag, flag is 1
4. Thread B decrements its copy of flag, also 2, and writes to flag, flag is 1.

你失去了一個減量。 你想使用__sync_fetch_and_sub (&flag,1),它將原子地遞減標志。

最后,圍繞睡眠旋轉並不是最好的方法。 你想要等待一個條件,等待一個信號 當工作線程意識到它們已經將標志遞減到0時,讓工作線程提高條件或信號。

暫無
暫無

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

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