boost::wait和boost::condition必须共享同一个互斥对象吗?

does boost::wait and boost::condition have to share the same mutex object

本文关键字:boost 同一个 对象 wait condition 共享      更新时间:2023-10-16
boost::condition_variable cond;
boost::mutex mutex;
//thread #1
for(;;)
{
    D * d = nullptr;
    while( cb.pop(d) )  //cb is a circular buffer and manage is own mutex/lock internally
    {
        //...do something with d
    }
    boost::unique_lock<boost::_mutex> lock( mutex );
    cond.wait( mutex );
}
//thread #2
while(1)
{
    getchar();
    for( int i = 0 ; i < 1000 ; ++i )
    {
        cb.push(new D(i));      //one producer so no lock required
        cond.notify_one();     // no lock required here ?
    }
}

我想知道如果我的数据容器有自己的锁来避免数据竞争,另一方面boost::wait使用他的锁/互斥锁机制,因为它由boost文档指定,如果它是ok的?

否则,thread1是消费者,如果我只有一个线程"消费",似乎等待所需的锁有点多余,不是吗?

EDIT:我不关心错过的更新。当我接收到更新时,我用接收到的数据更新对象。我只想要更新鲜的更新,不一定是所有的更新

可以有任意多的锁,但是会出现竞争条件除非poppush被同一个互斥锁保护waitnotify之间的锁不被释放决定等待和实际等待)。标准的用法是:

//  thread #1
// ...
{
    boost::unique_lock<boost::mutex> lock( mutex );
    while ( !cb.pop( d ) )
        cond.wait( mutex );
}
// process d

//  thread #2
// ...
{
    boost::unique_lock<boost::mutex> lock( mutex );
    cb.push( new D(i) );
    cond.notify_one();
}

尝试在线程#1中循环pop更复杂,至少如果您想在处理d期间释放锁。你需要比如:

boost::unique_lock<boost::mutex> lock( mutex );
while ( cb.pop( d ) ) {
    lock.unlock();
    //  process d
    lock.lock();
}
cond.wait( mutex );

它更复杂,我看不出你能从中得到什么。只是使用通常的模式,这是已知可靠的。

fww:你的代码充满了竞争条件:对于初学者:pop线程1失败,有一个上下文切换,线程2做pushnotify,然后返回到执行cond.wait的线程1。然后等待,尽管队列中有东西。

我可以补充一点,对于像这样的类型几乎没有任何正当理由循环缓冲区来管理它们自己的互斥锁。粒度为太低了。例外情况是,如果pop指令实际上等待,直到有些东西在那里,即(基于std::deque):

T* CircularBuffer::push( std::auto_ptr<T> in )
{
    boost::unique_lock<boost::mutex> l( myMutex );
    myQueue.push_back( in.get() );
    in.release();  // Only after the push_back has succeeded!
    myCondition.notify_all();
}
std::auto_ptr<T> CircularBuffer::pop()
{
    boost::unique_lock<boost::mutex> l( myMutex );
    while ( myQueue.empty() ) {
        myCondition.wait();
    }
    std::auto_ptr<T> result( myQueue.front() );
    myQueue.pop_front();
    return result;
}

(注意在接口中使用了auto_ptr。一旦提供者将对象传递到队列中,它不再具有访问权限它。)

condvar必须使用保护数据的互斥锁(好吧,不完全是,下面更详细),否则您将错过更新:

producer             consumer
                     while(cb.pop()) ...;
cb.push();
cond.notify_one();
                     cond.wait(); // OOPS. I missed the notification!

要避免这种情况,必须在消费者中:

  1. mutex
  2. 验证条件不满足
  3. cond.wait(mutex);
  4. 返回验证条件(mutex再次锁定)

和producer中必须:

  1. mutex
  2. 使条件为真(即cb.push())
  3. cond.notify_one()
  4. 现在你终于可以解锁mutex了。

所以它不一定是保护数据的锁,但是你必须在最后一次检查和等待中锁定,在生产者中设置条件和通知。

顺便说一句,实际上可以创建一个不需要与锁配合的通知机制。它需要对"为信号注册"answers"等待信号"进行单独的操作,后者在第一个信号出现后立即返回(并检查它们之间的条件)。然而,我还没有在任何可移植的线程库中看到这样的机制。

Edit:另一方面,信号量可能更适合于管理消息队列。具有反映队列中项目数量的信号量。您可以在每个pushdown之前为每个pop添加up(或者只是将其嵌入队列本身,因此pop将简单地等待如果队列为空出现的内容)。

如果环缓冲区的pushpop函数是线程安全的,那么您不需要额外的同步。

如果你有多个读取器,你可以使用一个读/写锁来允许多个线程同时读取。

通常,应该使用条件变量来表示线程之间共享的条件,因此应该以线程安全的方式访问条件。但是,互斥锁需要在等待时解锁,以便其他线程可以更改条件。请看这里的一个使用队列的例子。

在你的例子中,你已经有了一个线程安全容器。把条件变量放在容器中并让它使用它的互斥量不是更好吗?