2014-05-20 31 views
0

我有一個後臺線程在狀態變量done上循環。當我想停止線程時,我將變量done設置爲true。但顯然這個變量從未設置。我知道編譯器可能會優化它,所以我標記爲donevolatile。但這似乎沒有任何影響。請注意,我並不擔心競賽狀況,所以我還沒有製作它atomic或使用任何同步結構。我如何讓線程在每次迭代時都不跳過測試變量?還是完全是其他問題? done最初是false揮發性變量被優化了嗎?

struct SomeObject 
{ 
    volatile bool done_; 
    void DoRun(); 
}; 

static void RunLoop(void* arg) 
{ 
    if (!arg) 
     return; 

    SomeObject* thiz = static_cast<SomeObject*>(arg); 
    while(!(thiz->done_)) { 
     thiz->DoRun(); 
    } 
    return; 
} 
+3

請不要在多線程上下文中使用volatile:http://stackoverflow.com/questions/4557979/when-to-use-volatile-with-multi-threading – Ryp

+0

'我不擔心競爭條件' - 也許你應該。誰將done變量設置爲true? – ComicSansMS

+0

@Ryp所以,基本上'volatile'是無用的。那麼,我該如何完成我想要做的事? – 341008

回答

3

volatile在C++中沒有任何多線程含義。它是來自C的保留,用作信號處理程序觸及的sig_atomic_t標誌的修飾符,並用於訪問存儲器映射設備。對於C++函數來說,沒有語言強制的強制重新訪問內存,這導致了其他人注意到的競爭條件(讀者從不打擾兩次檢查爲「優化」)。

使用std::atomic(來自C++ 11或更新版本)。

它可以是,通常爲無鎖:

struct SomeObject { 
    std::atomic_bool done_; 
    void DoRun(); 
    bool IsDone() { return done_.load(); } 
    void KillMe() { done_.store(true); } 
}; 

static void RunLoop(void *arg) { 
    SomeObject &t = static_cast<SomeObject &>(*arg); 

    cout << t.done_.is_lock_free(); // some archaic platforms may be false 

    while (!t.IsDone()) { 
    t.DoRun(); 
    } 
} 

load()store()方法強制編譯器,至少,在每次迭代檢查存儲器位置。對於x86[_64]SomeObject實例的done_成員的高速緩存行將被緩存並在本地進行檢查,原樣沒有鎖定甚至原​​子/鎖定的內存讀取。如果你正在做一些比單向標誌更復雜的事情,你需要考慮使用諸如顯式內存隔離等。

Pre-C++ 11沒有多線程內存模型,所以您將不得不依賴具有特殊編譯器特權(如pthread)的第三方庫或使用特定於編譯器的功能來獲得等效。

+0

不應該'原子done_;'是'std :: atomic_bool done_;'? – paxos1977

+0

當然,爲什麼不。修復... – Jeff

+0

另一個評論,我認爲在這種情況下,load()和store()操作實際上可以放鬆。在大多數情況下,只要最終可以看到該值的變化,就不需要「停止」標誌成爲完整的內存屏障(如默認值)。爲此,我想我通常會使用load(std :: memory_order_relaxed)來檢查sentinel的值,像done。 – paxos1977

1

這工作就像你在msvc 2010期待的一樣。如果我刪除了volatile,它會永遠循環。如果我離開它的易變性,它的工作。這是因爲微軟像你期望的那樣處理易失性,這與iso不同。

這工作太:

struct CDone { 
    bool m_fDone; 
}; 

int ThreadProc(volatile CDone *pDone) { 
} 

這裏是MSDN說:被聲明爲揮發性

http://msdn.microsoft.com/en-us/library/12a04hfd.aspx

對象在一定的優化不能使用,因爲它們的值可以改變任何時候。即使先前的指令要求來自同一對象的值,系統始終會在請求時讀取易失性對象的當前值。

此外,對象的值立即寫入賦值。

ISO標準:

如果你熟悉C#volatile關鍵字,或熟悉的揮發性在早期版本的Visual C++的行爲,要知道,C++ 11的ISO標準volatile關鍵字是不同,並且在指定/ volatile:iso編譯器選項時在Visual Studio中受支持。 (對於ARM,它是默認指定的)。 C++ 11 ISO標準代碼中的volatile關鍵字僅用於硬件訪問;請勿將其用於線程間通信。對於線程間通信,使用C++標準模板庫中的std :: atomic等機制。