2012-07-18 74 views
0

我知道如何使用條件變量(這個構造的混淆名稱,IMO,因爲cv對象既不是變量也不指示條件)。所以,我有一對螺紋,canonically設置了Boost.Thread爲:需要通過條件變量尋找互斥保護(原子)分配?

bool awake = false; 
boost::mutex sync; 
boost::condition_variable cv; 
void thread1() 
{ 
    boost::unique_lock<boost::mutex> lock1(sync); 
    while (!awake) 
     cv.wait(lock1); 
    lock1.unlock(); // this line actually not canonical, but why not? 
    // proceed... 
} 
void thread2() 
{ 
    //... 
    boost::unique_lock<boost::mutex> lock2; 
    awake = true; 
    lock2.unlock(); 
    cv.notify_all(); 
} 

我的問題是:線程2確實需要保護分配給awake?在我看來notify_all()調用應該是足夠的。如果被操縱和檢查的數據不僅僅是一個簡單的「可以繼續」標記,我可以看到互斥體中的值,但這裏看起來像是矯枉過正。

問題的第二個問題是在代碼片段中問:爲什麼不在Boost文檔中顯示thread1的鎖在「process data」步驟之前被解鎖?

編輯:也許我的問題是真的:是否有比簡歷更清潔的構造來實現這種等待?

回答

1

確實需要將thread2保護到分配狀態才能喚醒?

是的。從一個線程修改對象並在沒有同步的情況下從另一個線程訪問它會導致未定義的行爲。即使它只是一個bool

例如,在某些多處理器系統上,寫操作可能隻影響本地內存;沒有明確的同步操作,其他線程可能永遠不會看到更改。

爲什麼在「過程數據」步驟之前,Boost文檔沒有顯示正在解鎖的thread1中的鎖?

如果您在清除標誌前解鎖了互斥鎖,那麼您可能會錯過另一個信號。

是否有比CV更清潔的構造來實現這種等待?

在Boost和標準C++庫中,沒有;一個條件變量足夠靈活來處理任意的共享狀態,對於這個簡單的情況來說並不特別複雜,所以不需要特別簡單。

更一般地,您可以使用信號量或管道在線程之間發送簡單信號。

+0

這是記憶同步的事情,讓我絆倒。關於您的最後一點:信號量(通過該名稱)不是Boost或C++ 11線程的一部分; Boost.InterProcess有一個。目前還不清楚它如何更簡單。同樣,我不會爲一個進程使用管道。 – 2012-07-18 19:15:44

+0

@MikeC:沒錯,在Boost或C++線程庫中沒有信號量,就像我的回答說的那樣。從概念上講,信號量或管道可能被認爲是稍微更清潔的,因爲它是單個對象,並且不涉及任何外部共享狀態。就我個人而言,我更喜歡通過消息隊列等進行更高級別的同步,避免顯式共享狀態,但遺憾的是沒有將其納入此版本的標準。 – 2012-07-18 20:08:27

1

形式上,你肯定需要在兩個線程鎖:如果任何線程 修改對象,而且不止一個線程訪問它,然後所有 訪問必須同步。

在實踐中,你可能會在沒有鎖的情況下逃脫;它的 幾乎肯定notify_all將發出必要的fence或 membar指令來確保內存正確同步。 但爲什麼冒這個險?

至於unlock的由於缺少,這是作用域 鎖定模式的整點:unlock是在對象的析構函數,所以 ,即使異常穿過互斥將被解鎖。

+0

好吧,顯然如果後續處理將執行額外的操作,這將涉及在同一個互斥體上進一步同步,則必須使用解鎖。實際上,我並不太喜歡有限範圍的鎖定,因爲這個原因以及它鼓勵的醜陋的內聯塊編碼;我更喜歡Java語法。 – 2012-07-18 19:19:03

+0

@MikeC:如果你做了任何可能拋出異常的作用域,鎖定是必不可少的,除非你使用更醜陋的try/catch/rethrow塊 - 這就是我不喜歡Java語法的原因。 – 2012-07-18 20:40:21

+0

@MikeSeymour:我們必須同意對非關鍵字綁定的開閉括號結構與帶有try和catch等關鍵字的相對醜陋性的不同意見。爲了引入範圍而進行縮進只是簡單的黑客行爲。 :)另外,我也喜歡Python的'with'。 – 2012-07-18 21:00:37