2015-04-16 51 views
6

可以從m_flag的定義這裏刪除volatile安全嗎? 如果m_flag不是易失性的,什麼會阻止編譯器優化掉這個循環的條件:while (!m_flag) m_cv.wait(lock);? 標準(post-C++ 11)明確指出在這種情況下禁止這種優化嗎?在這裏刪除C++ volatile是否安全?

#include <mutex> 
#include <condition_variable> 
#include <future> 
#include <iostream> 
using namespace std; 

class foofoo 
{ 
    volatile bool m_flag; 
    mutex m_mutex; 
    condition_variable m_cv; 

public: 
    void DoWork() 
    { 
     m_flag = false; 
     unique_lock<mutex> lock(m_mutex); 
     auto junk = async(std::launch::async, [this]() 
     { 
      { 
       unique_lock<mutex> lock(m_mutex); 
       m_flag = true; 
      } 
      m_cv.notify_one(); 
     }); 
     while (!m_flag) m_cv.wait(lock); 
     cout << "ququ" << endl; 
    } 
}; 

int main() 
{ 
    foofoo f; 
    f.DoWork(); 
} 

回答

10

一般來說,volatile和多線程在C++ 11中是正交的。使用volatile既不增加也不移除數據競賽。

在這種情況下,被m_flag = true;之前測序互斥的由async([intro.execution]/P14)發起的線程,這又與同步互斥的後續獲取m_cv.wait(lock)釋放([thread.mutex.requirements.mutex]/p11,25),其依次是,在之前測序,隨後讀取m_flagm_flag = true;因此inter-thread發生在之前,因此發生在之前,隨後讀取。 ([intro.multithread]/p13-14)

由於有上m_flag沒有其他副作用,m_flag = true;可見副作用相對於該讀出([intro.multithread]/P15),並且因此閱讀必須讀取由可見副作用存儲的內容,即true

不管是否使用volatile,「優化」掉該條件的編譯器都是不合格的。

+0

謝謝!換句話說,如果循環的主體不改變循環條件,但包含同步「點」,爲了優化它,編譯器將不得不分析循環條件是否可能受到「可見側效果「從另一個可以同步」點「的線程?在實踐中,編譯器可能會放棄,永遠不會試圖優化這種循環? –

+0

我的意思是「優化循環的條件」。我剛剛意識到我到處都在說「循環」,但很顯然循環將保持,只是變成一個無限循環... –

+2

@MaxGalkin只要你不寫數據競爭,編譯器必須確保更改由先前釋放互斥體的線程創建的線程對隨後獲取相同互斥體的線程可見。我不確定我是否會說「永遠不要嘗試」,但只要代碼沒有數據競爭,就不會有符合編譯器的意外。 –

1

您在循環內調用wait,因此編譯器無法消除它。除了wait可能對編譯器來說或多或少不透明的事實之外,它還包含互斥鎖,它可以有效地防止任何消除編譯器。所以volatile在那裏完全沒用。

+0

我認爲如果你在該方法中使用本地布爾變量而不是m_flag,編譯器可以自由地優化環路條件,因爲局部變量對其他線程是不可見的......「等待「在循環體內... –

+1

如果你使用本地,那麼它可能會 - 我不是一個編譯器專家。編譯器消除任何東西(據我所知)的唯一方法是證明變量不能在其他地方更改。任何對不透明函數的調用都可能會極大地阻礙這種能力,編譯器會放棄嘗試。如果你使用局部變量,那麼如果你沒有在任何地方通過它的地址,那麼它肯定沒有機會在任何其他地方設置。 – ixSci

+0

然後你的回答中的這個陳述是不正確的:「你在循環內部調用了'wait',所以編譯器沒有辦法消除它。」只是打電話給'等待'是不夠的,以防止消除... –