2015-12-22 122 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的重复呼叫有点红旗 - 它们表示您在轮询而不是等待。我认为解决的办法是你需要两个条件变量。生产者等待一个直到被消费者通知,消费者等待另一个生产者通知。我不太清楚你如何让这个工作与多个消费者虽然。