使用boost条件变量

时间:2013-02-07 13:52:55

标签: c++ boost

我正在尝试使用代码来测试同步化的boost条件变量, 这段代码确实同步。但它只显示4个值这里有什么问题? ,我怎么能解决它?

在Windows 7上使用vs 2012

提前感谢。

#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();

}

4 个答案:

答案 0 :(得分:6)

问题是班级生产者/消费者问题。你的代码实际上没有意义 - 你不能使用单个条件变量来等待双方,(记住你在相同的情况下调用notify然后等待 - 在相同的条件下。)所以任何事情都可能发生。 / p>

您需要重构代码以使用两个条件变量,一个用于生产者,一个用于消费者,例如:

编辑:更新了纯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的需求。

答案 1 :(得分:3)

Nim对于需要单独的生产者和消费者condition_variables是正确的,但是等待条件的答案中的原始代码是有缺陷的[见下面的注释]这就是为什么vivek需要使用timed_wait(不是理想的解决方案,BTW)。

Nim已经修复了代码,所以如果您使用的是C ++ 11,Nim的答案中的代码是一个很好的解决方案。

如果你遇到旧版本的C ++,下面的代码就可以了。

使用condition_variable的常用习惯是:

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

并且,是的,误报是可能的,这就是为什么上面的代码重新测试&#34;准备好去&#34;从condition_variable.wait()调用返回后。

使用Nim使用的函数对象(在本例中为lambda函数)重载的boost :: wait方法实现了这个习惯用法,包括忽略误报(仍然会发生)。它使用函数对象来测试&#34;准备就绪&#34;条件,并且在重新获得锁定并重新测试该函数之前不会返回。

下面的代码适用于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

注意:为了帮助其他人浏览这个棘手的区域(并看看大惊小怪),原始答案在显示方法中包含与此类似的代码:(添加了行#&#39;并省略了一些细节。)

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行等待时,应该使其唤醒的通知已经发生,因此等待将永远挂起。

答案 2 :(得分:0)

程序显示5个值而不是4

0
1
2
3
4

你的循环

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

通知仅在此处通知,仅通知10次中的5次

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

这个条件是两个以上的。

要修复它,一切都取决于你想做什么。如果您希望收到10次通知,请将计数器从10更改为20; - )

答案 3 :(得分:-1)

之前只需添加boost :: this_thread :: sleep(boost :: posix_time :: millisec(10) boost :: mutex :: scoped_lock lock(m); 在增量函数中。 一点点同步问题