2016-02-27 73 views
0

看起來好像condition_variablenotify_one並不總是按照它應該的方式工作。condition_variable不總是正常工作

struct Task { 
    std::mutex mutex; 
    std::condition_variable cv; 
    std::atomic_bool launch{false}; 
}; 
void job(Task& task) { 
    std::unique_lock<std::mutex> lock{task.mutex}; 
    task.cv.wait(lock, [&]{ return task.launch == true; }); 
} 
int main() { 
    for (auto i=0 ; i<1000*1000 ; i++) { 
     Task task; 
     std::thread thread{job, std::ref(task)}; 
     task.launch = true; 
     task.cv.notify_one(); 
     thread.join(); 
    } 
} 

這個程序幾乎永遠不會結束,絕大多數時間它會永遠停留在循環中。 這是爲什麼發生?

+1

發生了什麼,你期望發生什麼?這些是一個很好的問題的基本部分... –

+0

謝謝@UlrichEckhardt,我希望程序能夠完成,而且幾乎從來不會。 –

+0

它究竟在哪裏掛?哪一行是成功執行的最後一行? –

回答

4

兩個錯誤的位置:

  • 如果一個對象同步訪問,你並不需要的原子類型爲對象。您的atomic_bool只會導致開銷。
  • 如果要同步對launch標誌的訪問權限,則需要先鎖定其互斥量,然後再寫入該互斥量。你不這樣做,在main()

說明:

  1. main()創建task
  2. main()創建thread
  3. job()鎖定互斥
  4. job()檢查launch,這是假
  5. main()集小號launch
  6. main()信號的CV,這沒有人接收
  7. job()等待CV由於launch在步驟值4

通常,步驟3和6是原子,因爲任何其他線程應該沒有鎖住互斥鎖,沒有觸及launch。由於這種情況沒有發生,允許進行依賴操作的交織,這最終導致意外行爲。

+1

因此,具體來說,job()在步驟6等待CV的* reason *是因爲'main'函數沒有將寫入與'launch'同步,所以線程不一定能看到變化?如果線程看到'launch'爲true,那麼它永遠不會阻塞條件變量,因爲'condition_variable :: wait'在等待之前檢查謂詞。 –

+0

思考它,也許我的解釋是錯誤的,@SteveJessop。此外,你的也不會改善它,因爲'原子'應該是安全的。我會添加另一個解釋。 –

+1

這就是我一直在努力解決的問題。我現在認爲我的問題的答案是「不」。原子類型意味着CV上的阻塞相對於'main'寫入'launch'而被排序。但是'main'通知CV之後,仍然沒有什麼可以對CV上的線程進行排序。 –

1

不清楚你想要什麼,但是你的問題是,在線程有時間撥打wait之前,主要可能會發生launch=truenotify_one()。在這種情況下,您應該知道notify未延遲,因此您的主將在join上被阻止,而線程在wait上被阻止。