2013-02-07 59 views
0

我想下面的代碼來測試升壓條件變量的synochonization, 這段代碼沒有同步。但它只顯示了4個值這裏有什麼問題? ,我該如何解決它?使用升壓條件變量

使用vs 2012在Windows 7上

在此先感謝。

#include <iostream> 
#include <queue> 

#include "boost\thread.hpp" 
#include "boost\timer.hpp" 

using namespace std; 

int counter; 

boost::mutex m; 
boost::condition_variable CworkDone; 
bool workdone = true; 

bool procOn = true; 

void display() 
{ 
while (procOn == true) 
{ 
    boost::mutex::scoped_lock lock(m);  

    if (workdone) 
    { 
     cout<<counter<<endl; 
     CworkDone.notify_one(); 
     workdone = false; 
    } 
    else 
    { 
     CworkDone.wait(lock); 
    } 
} 

} 

void increment() 
{ 
for(int i = 0 ; i <10 ; ++i) 
{ 

    boost::mutex::scoped_lock lock(m); 

    if (!workdone) 
    { 
     boost::this_thread::sleep(boost::posix_time::millisec(500)); 
     ++counter; 
     workdone = true; 
     CworkDone.notify_one(); 
    } 
    else 
    { 
     CworkDone.wait(lock); 
    } 
} 
procOn = false; 
} 

    int main() 
{ 
boost::thread dispthread(display); 
boost::thread incthread(increment); 
dispthread.join(); 
incthread.join(); 

} 

回答

6

問題是類生產者/消費者問題。你的代碼現在並沒有什麼意義 - 你不能使用單個條件變量來等待雙方,(記住你在相同的上下文中調用notify和wait - 在相同的條件下)。所以什麼都可能發生。

你需要重構你的代碼使用兩個條件變量,一個是生產,一個是消費,例如:

編輯:更新與純C++ 11的實現應該是正確的。

#include <atomic> 
#include <chrono> 
#include <condition_variable> 
#include <iostream> 
#include <mutex> 
#include <thread> 

using namespace std; 

mutex mutex_; 
condition_variable con_, prd_; 
atomic_bool done_{false}, process_{false}; // possibly overkill, but hey-ho 
int counter = 0; // protected resource 

void consumer() 
{ 
    for (;;) 
    { 
    unique_lock<mutex> lock(mutex_);  // acquire the mutex before waiting 
    // this is the correct way to wait on the condition (as highlighted by @Dale) 
    con_.wait(lock, []{ return process_.load() || done_.load(); }); 
    if (!done_.load()) 
    { 
     cout << counter << endl; 
     process_.store(false);    // we're done with this item 
     lock.unlock();      // release before notifying 
     prd_.notify_one(); 
    } 
    else 
     break;        // we're done completely 
    } 
} 

void producer() 
{ 
    unique_lock<mutex> lock(mutex_); 
    for (int i = 0; i < 10; ++i) 
    { 
    this_thread::sleep_for(chrono::milliseconds(500)); 
    ++counter; 
    process_.store(true);     // indicate that there is data 
    con_.notify_one(); 
    // wait for the consumer to complete 
    prd_.wait(lock, []{ return !process_.load(); }); 
    } 
    done_.store(true);      // we're done 
    lock.unlock();       // release before notifying 
    con_.notify_one(); 
} 

int main(void) 
{ 
    thread c(consumer); 
    producer(); 
    c.join(); 
} 

所以,現在發生的事情是,消費者等待它的條件(也是唯一的生產者會調用這個notify()),和製片人,一旦產生一些調用此通知,這將喚醒客戶端了。然後客戶會致電notify()喚醒生產者。

以上更新的方法不應該像我以前強調的那樣出現虛假喚醒問題。這抵消了對timed_wait的需求。

+0

感謝您的回覆,但它沒有奏效。 – vivek

+0

不知何故代碼簡單地掛在打印一些值後,但如果我一步一步調試的東西工作正常。 – vivek

+0

@vivek - 現在看到答案.. – Nim

0

該方案顯示5個值不是4

0 
1 
2 
3 
4 

的通知僅此

if (!workdone) 
{ 
    boost::this_thread::sleep(boost::posix_time::millisec(500)); 
    ++counter; 
    workdone = true; 
    CworkDone.notify_one(); 
} 

這做你的循環

void increment() 
{ 
for(int i = 0 ; i <10 ; ++i) 

通知只有5個10倍條件是真實的一兩個。

要解決這個問題,一切都取決於你想做什麼。如果要通知10次將計數器從10更改爲20 ;-)

+0

感謝您的回覆,我試過但循環沒有退出。任何方式我需要執行10次處理。並顯示10個值。 – vivek

-1

只需添加boost :: this_thread :: sleep(boost :: posix_time :: millisec(10)之前 boost :: mutex :: scoped_lock的鎖(M); 在遞增功能 一點點的同步化問題

+1

如果我以爲你有一種非常乾燥的幽默感,我會投票贊成這個答案,但我的一部分人認爲你是認真的。 – Zak

3

稔說得對,需要獨立的生產者和消費者condition_variables,但在這個問題的答案原來的代碼等待條件是有缺陷的[參見下面的註釋]這就是爲什麼vivek需要使用timed_wait(不是一個理想的解決方案,BTW)。

Nim已經修復了代碼,所以如果你是usi ng C++ 11 Nim的答案中的代碼是一個很好的解決方案。

如果你被困在老版本的C++中,下面的代碼將工作。

使用一個condition_variable的常見的成語是:

lock the mutex 
while (not ready to go) 
    condition_variable.wait() 
do it 

而且,是的,誤報可能這就是爲什麼上面的代碼複試「蓄勢待發」,從condition_variable.wait回國後()調用。

用nim使用的函數對象(本例中爲lambda函數)重載的boost :: wait方法實現了這個習慣用法,並且包括忽略誤判(仍然發生)。它使用函數對象來測試「準備好」條件,並且在它重新獲得鎖並重新測試該函數之前不會返回。

下面的代碼在pre-C++ 11中工作,並且不使用函數基於對象的boost等待來清除實際發生的事情。

#include <boost/thread.hpp> 

boost::mutex m; 
boost::condition_variable cProducer; 
boost::condition_variable cConsumer; 
volatile bool readyToDisplay = false; 
volatile bool done = false; 

void display() 
{ 
    boost::mutex::scoped_lock lock(m); 
    while(!done) 
    { 
     while (!readyToDisplay) 
     { 
      cConsumer.wait(lock); 
     } 
     cout << counter << endl; 
     readyToDisplay = false; 
     cProducer.notify_one(); 
    } 
} 

void increment() 
{ 
    while(!done) 
    { 
    boost::this_thread::sleep(boost::posix_time::millisec(500)); 
    boost::mutex::scoped_lock lock(m); 
    while(readyToDisplay) 
    { 
     cProducer.wait(lock); 
    } 
    ++counter; 
    readyToDisplay = true; 
    done = (counter == 10); 
    cConsumer.notify_one(); 
    } 
} 

int main() 
{ 
    boost::thread dispthread(display); 
    boost::thread incthread(increment); 
    dispthread.join(); 
    incthread.join(); 
    return 0; 
} 

輸出:

1 
2 
3 
4 
5 
6 
7 
8 
9 
10 

注:爲了幫助別人瀏覽這個棘手的區域(看看有什麼大驚小怪的)原來的答案包含類似於此代碼的顯示方法: (添加了#行,省略了一些細節)。

1: while (!done) 
2: { 
3: boost::mutex::scoped_lock lock(m); 
4: cConsumer.wait(lock); 
5: std::cout << counter << endl; 
6: cProducer.notify_one(); 
7: } 

假設在該線程執行之前3另一個線程增加計數器並在條件變量上調用notify_one。當這個線程鎖定第3行上的互斥鎖並等待第4行時,應該讓它喚醒的通知已經發生,所以等待將永遠掛起。