【问题标题】:Using condition variable in a producer-consumer situation在生产者-消费者情况下使用条件变量
【发布时间】:2011-01-23 16:50:18
【问题描述】:

我正在尝试了解条件变量以及如何在生产者-消费者情况下使用它。我有一个队列,其中一个线程将数字推入队列,而另一个线程从队列中弹出数字。当生产线程放置一些数据时,我想使用条件变量向消费线程发出信号。问题是有时(或大多数时候)它最多只能将两个项目推入队列然后挂起。我已经在生产()函数中指出它在调试模式下运行时停止的位置。谁能帮我指出为什么会这样?

我有以下全局变量:


boost::mutex mutexQ;               // mutex protecting the queue
boost::mutex mutexCond;            // mutex for the condition variable
boost::condition_variable condQ;

下面是我的消费者线程:


void consume()
{
    while( !bStop )   // globally declared, stops when ESC key is pressed
    {
        boost::unique_lock lock( mutexCond );
        while( !bDataReady )
        {
            condQ.wait( lock );
        }

        // Process data
        if( !messageQ.empty() )
        {
            boost::mutex::scoped_lock lock( mutexQ );

            string s = messageQ.front();   
            messageQ.pop();
        }
    }
}

以下是我的生产者线程:


void produce()
{
    int i = 0;

    while(( !bStop ) && ( i &lt MESSAGE ))    // MESSAGE currently set to 10
    {
        stringstream out;
        out &lt&lt i;
        string s = out.str();

        boost::mutex::scoped_lock lock( mutexQ );
        messageQ.push( s );

        i++;
        {
            boost::lock_guard lock( mutexCond );  // HANGS here
            bDataReady = true;
        }
        condQ.notify_one();
    }
}

【问题讨论】:

  • FWIW,这个错误是“锁反转”的完美例子。在一个线程中,您首先锁定 mutexQ,然后在不释放它的情况下锁定 mutexCond。在另一个线程中,您先锁定 mutexCond,然后再锁定 mutexQ。这几乎总是错误的。一个线程同时获得一个锁,而另一个线程同时获得另一个。然后他们都会等待获得他们没有得到的锁。他们永远无法得到。简单的解决方案是使用更少的锁(在这种情况下无论如何都是正确的,即使不是为了反转),或者定义锁的“层次结构”,这样您就可以始终以一致的顺序使用它们。

标签: c++ boost multithreading condition-variable


【解决方案1】:

您必须使用与在条件变量中使用的相同的互斥锁来保护队列。

这应该就是你所需要的:

void consume()
{
    while( !bStop )
    {
        boost::scoped_lock lock( mutexQ);
        // Process data
        while( messageQ.empty() ) // while - to guard agains spurious wakeups
        {
            condQ.wait( lock );

        }
        string s = messageQ.front();            
        messageQ.pop();
    }
}

void produce()
{
    int i = 0;

    while(( !bStop ) && ( i < MESSAGE ))
    {
        stringstream out;
        out << i;
        string s = out.str();

        boost::mutex::scoped_lock lock( mutexQ );
        messageQ.push( s );
        i++;
        condQ.notify_one();
    }
}

【讨论】:

  • @nos:谢谢,您的解决方案有效。这是条件变量最常见的应用/用途,对吧?
  • "// while - 防止虚假唤醒" 您能否解释一下为什么需要一个 while 循环。这是否意味着 wait() 可以在没有生产者发出信号的情况下返回?
  • @Haider 在某些情况下,是的——尽管在这个小例子中可能不是。他们在这里有所解释pubs.opengroup.org/onlinepubs/7908799/xsh/…,这是pthreads而不是boost - 但是*nixes上的boost使用pthreads并且不能真正采取任何措施来对付它。使用 while() 而不是 if() ,更容易将代码更改为拥有多个消费者,在这种情况下,如果另一个消费者在你之前抢到了价值,你最终会做正确的事情。 (如果你有多个消费者, condQ.notify_one() 可以唤醒多个消费者)
  • @nos:我知道这是一个老话题,但我有一个问题。我假设你的想法是你可以通过设置 bStop = true 来停止进程,但是如果因为队列是空的,consume() 在此时等待怎么办?生产者停止生产,因此消费者不再获得任何数据而只是等待。因此它不会检查 bStop 变量。我的问题清楚吗?你会如何解决这个问题?
  • @PolyVox 没错,所以你需要发送消息唤醒它以及设置 bStop。它甚至可能是消费者识别为“停止”消息的特殊消息。
猜你喜欢
  • 1970-01-01
  • 2015-02-07
  • 2017-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多