如果在wait()之前调用notify()会发生什么?

27

我有一种情况,即在等待()之前可能会调用notify()。

当我通过发送消息通知它时,我正在尝试制作一个模拟器以安排其下一个事件。因此,我已经设计了等待->通知->调度链

void Broker::pause()
{
    boost::unique_lock<boost::mutex> lock(m_pause_mutex);
    {
        std::cout << "pausing the simulation" << std::endl;
        m_cond_cnn.wait(lock);
        std::cout << "Simulation UNpaused" << std::endl;
        // the following line causes the current function to be called at 
        // a later time, and a notify() can happen before the current function
        // is called again
        Simulator::Schedule(MilliSeconds(xxx), &Broker::pause, this);
    }
}

void Broker::messageReceiveCallback(std::string message) {
    boost::unique_lock<boost::mutex> lock(m_pause_mutex);
    {
        m_cond_cnn.notify_one();
    }
}

问题在于:在调用wait()之前可能会调用notify()。

针对这种情况是否有解决方案? 谢谢


参见:是否有notify_one()队列? - Gabriel Staples
2个回答

24

条件变量很难单独使用,因为它们只唤醒当前等待的线程,正如您所注意到的那样。此外还有虚假唤醒的问题(即条件变量有时会唤醒一个线程,而没有任何对应的notify被调用)。为了正常工作,条件变量通常需要另一个变量来维护更可靠的状态。

为了解决这两个问题,在您的情况下,您只需要添加一个布尔标志:

boost::unique_lock<boost::mutex> lock(m_pause_mutex);
while (!someFlag)
    m_cond_cnn.wait(lock);
someFlag = false;

//...

boost::unique_lock<boost::mutex> lock(m_pause_mutex);
someFlag = true;
m_cond_cnn.notify_one();

1
我没错吧,这种“带标志的条件变量”其实就是一个最大级别为1的信号量? - alexeykuzmin0
2
我不明白为什么C++11和pthreads称之为“条件变量”,虽然它完全是无状态的,并且像一个裸信号/事件一样工作,而WinAPI称之为“事件”,但在那里它的工作方式却像一个同步布尔标志。难道这不应该是相反的吗? - stgatilov
那个标志不需要是std::atomic<bool>吗? - kroiz
3
@kroiz 在这种情况下不需要:访问受互斥锁保护。如果某些代码在未先锁定互斥锁的情况下访问它,则需要原子操作。 - syam

3
我认为syam的回答总体上是可以的,但在你使用ns-3的特定情况下,我建议您重构代码以使用ns-3中正确的原语:
  1. 我怀疑您正在使用ns-3实时模拟器之一。很好。
  2. 安排一个0.1秒的保持活动事件,以确保模拟器保持运行(当没有剩余的事件时,它将停止运行)。
  3. 可选地,在此keepalive事件中使用布尔值检查是否应重新安排keepalive事件或调用Simulator :: Stop。
  4. 创建一个线程来运行带有Simulator :: Run()的模拟器主循环。模拟器将休眠,直到下一个计划的事件应该过期或直到外部安排了新事件
  5. 使用Simulator :: ScheduleWithContext从另一个线程外部安排事件。
请记住,通常情况下ns-3 API不是线程安全的。唯一线程安全的ns-3 API是ns3 :: Simulator :: ScheduleWithContext。我无法强调不要从不是主线程的线程中使用ns-3 ::命名空间中提供的任何其他API的重要性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接