为什么在通知condition_variable之前需要获取锁来修改共享的"原子"变量

kzh*_*dev 8 c++ condition-variable c++11

根据cppreference.com:

打算修改变量的线程必须

  1. 获取std :: mutex(通常通过std :: lock_guard)
  2. 在锁定时执行修改
  3. 在std :: condition_variable上执行notify_one或notify_all(不需要保持锁定以进行通知)

即使共享变量是原子的,也必须在互斥锁下对其进行修改,以便将修改正确地发布到等待的线程.

我不太明白,为什么修改原子变量需要锁定.请参阅以下代码段:

static std::atomic_bool s_run {true};
static std::atomic_bool s_hasEvent {false};
static std::mutex s_mtx;
static std::condition_variabel s_cv;


// Thread A - the consumer thread
function threadA()
{
    while (s_run)
    {
        {
            std::unique_lock<std::mutex> lock(s_mtx);
            s_cv.wait(lock, [this]{
                return m_hasEvents.load(std::memory_order_relaxed);
            });
        }

        // process event
        event = lockfree_queue.pop();
        ..... code to process the event ....
    }
}


// Thread B - publisher thread
function PushEvent(event)
{
    lockfree_queque.push(event)
    s_hasEvent.store(true, std::memory_order_release);
    s_cv.notify_one();
}
Run Code Online (Sandbox Code Playgroud)

在PushEvent函数中,我没有获取s_mtx,因为s_hasEvent是一个原子变量,而队列是lockfree.无法获得s_mtx锁定的问题是什么?

Jon*_*ely 7

正如Yakk对你所关联的问题的回答所指出的那样,是为了防止这一系列事件导致错过唤醒:

    1. 线程A锁定互斥锁.
    1. 线程A调用lambda的闭包,它执行m_hasEvents.load(std::memory_order_relaxed);并返回值false.
    1. 线程A被调度程序中断,线程B开始运行.
    1. 线程B将事件推入队列并存储到 s_hasEvent
    1. 线程B运行s_cv.notify_one().
    1. 线程B被调度程序中断,线程A再次运行.
    1. 线程A评估false闭包返回的结果,确定没有挂起事件.
    1. 线程A条件变量上的块,等待事件.

这意味着notify_one()错过了调用,即使队列中有事件准备就绪,条件变量也会阻塞.

如果在互斥锁被锁定时完成对共享变量的更新,则步骤4不可能在步骤2和7之间发生,因此条件变量对事件的检查会得到一致的结果.使用发布者和消费者使用的互斥锁要么s_hasEvent在第1步之前发生存储(因此闭包加载值true并且永远不会阻塞条件变量),要么在步骤8之后发生(因此notify_one()调用将唤醒它) .

  • 是。需要互斥体以防止与条件变量发生竞争。它与条件本身无关,也与它的原子性无关。 (2认同)

kzh*_*dev 0

在另一个线程中找到了关于这个问题的很好的解释。看一眼

下面询问有关竞争条件的问题。

如果所通信的数据是原子的,那么我们不能在“发送”端没有互斥体吗?

在最后。