2015-12-22 153 views
0
#include <thread> 
#include <mutex> 
#include <condition_variable> 
#include <iostream> 

std::mutex globalMutex; 
std::condition_variable globalCondition; 
int global = 0; 
int activity = 0; 
int CountOfThread = 1; // or more than 1 

// just for console display, not effect the problem 
std::mutex consoleMutex; 

void producer() { 
    while (true) { 
     { 
      std::unique_lock<std::mutex> lock(globalMutex); 
      while (activity == 0) { 
       lock.unlock(); 
       std::this_thread::yield(); 
       lock.lock(); 
      } 
      global++; 
      globalCondition.notify_one(); 
     } 
     std::this_thread::yield(); 
    } 
} 


void customer() { 
    while (true) { 
     int x; 
     { 
      std::unique_lock<std::mutex> lock(globalMutex); 
      activity++; 
      globalCondition.wait(lock); // <- problem 
      activity--; 
      x = global; 
     } 
     { 
      std::lock_guard<std::mutex> lock(consoleMutex); 
      std::cout << x << std::endl; 
     } 
     std::this_thread::sleep_for(std::chrono::seconds(1)); 
    } 
} 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
    for (int i = 0; i < CountOfThread; ++i) { 
     std::thread(customer).detach(); 
    } 
    std::thread(producer).detach(); 
    getchar(); 
    return 0; 
} 

我想是確保每次有客戶線程獲得一個全球性增加,預計像顯示:1,2,3,......,但我看到的是在等待和活動之間會增加全局值 - 因此,實際顯示爲:1,23,56,78,...C++:條件變量等待

我已經發現問題出在wait(),acutully there在wait(),'unlock,wait,lock'之間的3個步驟,在signaled(wait return)和mutex.lock之間,它不是原子操作,生產者線程可能在wait()鎖定互斥之前鎖定互斥,仍然不爲零,所以全球意外增加,意外

有沒有辦法確定我的期望?

+0

祝賀。它幫助極大! –

回答

0

我發現它可以幫助我在線程的上下文中。例如,如果你是一個客戶,在等什麼?對你而言,我的意思是在線程的上下文中。當你這樣想時,它可以使用monitors進行編碼變得非常簡單。

現在,正如馬丁所說,我認爲重複調用thread.yield有點嚇人。這可能會導致代碼的可怕交織。

爲了表明,爲什麼你的代碼不能正常工作,讓我們走過一個快速交織的例子:

  1. 一些客戶創建,並抓住鎖增加activity之一。然後該線程由於致電wait而進入睡眠狀態。
  2. 在初始客戶線程致電wait後,另一個線程被喚醒。這是因爲wait解鎖了傳入它的互斥鎖。
  3. 該線程獲取globalMutex並增加activity。然後它等待。
  4. 重複執行CountOfThread線程數,因爲這完全可能與多線程代碼。
  5. 最後,生產者線程運行,看到activity == 0,並解鎖。然而,它沒有notify_one(即信號)。然後它收益。這可能導致未定義和混淆的代碼。

我的建議:

  1. ,一定不要調用yield,同時在條件等待。這會導致複雜且難以讀取無效的監視器代碼。
  2. 想想每個線程正在等待什麼條件。如果代碼的不同部分在不同條件下等待,請創建一個不同的鎖。僅對一個共享代碼使用一個鎖。
  3. 在某些情況下使用不同條件變量。如果條件依賴於不同的數據,則必須使用不同的條件變量。

在您的情況下,解決方案並不像您想象的那麼複雜。使用我原來的建議:在線程的上下文中思考。例如,當生產者線程正在運行時,當客戶沒有注意到global已被更改爲時,它想要等待。當客戶線程正在運行時,只要生產者沒有改變,就要等待global

這是你想要的行爲的一個例子:上發佈一個完整的小例子

mutex m; 

condition_variable cv; 
int global = 0, prev_global = 0; 

void producer() 
{ 
    while (true) 
    { 
     unique_lock<mutex> lock(m); 

     while (prev_global != global) 
     { 
      cv.wait(lock); 
     } 
     prev_global = global++; 
     cv.notify_one(); 
     lock.unlock(); 
    } 
} 

void customer() 
{ 
    while (true) 
    { 
     unique_lock<mutex> lock(m); 

     while (prev_global == global) 
     { 
      cv.wait(lock); 
     } 

     prev_global = global; 
     cv.notify_one(); 
     lock.unlock(); 
    } 
} 

int main() 
{ 
    vector<thread> pool; 
    for (int i = 0; i < 5; ++i) 
    { 

     pool.push_back(thread (customer)); 
    } 

    pool.push_back(thread (producer)); 

    for (auto it = pool.begin(); it != pool.end(); ++it) 
    { 
     it->join(); 
    } 
    return 0; 
} 
+0

非常有幫助,這真的是我需要的,thx很多。 – Benjamin

+0

@Benjamin偉大,很高興我能幫上忙!你會將問題標記爲已回答嗎? –

1

你的問題是當activity> 0時,producer可以循環抓取鎖,遞增全局並通知條件變量。 (通知不必有相應的服務員)。

您對thread.yield的重複呼叫有點紅旗 - 它們表示您在輪詢而不是等待。我認爲解決的辦法是你需要兩個條件變量。生產者等待一個直到被消費者通知,消費者等待另一個生產者通知。我不太清楚你如何讓這個工作與多個消費者雖然。