当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《为什么在通知condition_variable之前需要获取锁来修改共享的"原子"变量》经验,为你挑选了1个好方法。

根据cppreference.com:

打算修改变量的线程必须

    获取std :: mutex(通常通过std :: lock_guard)

    在锁定时执行修改

    在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 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();
}

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



1> Jonathan Wak..:

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

    线程A锁定互斥锁.

    线程A调用lambda的闭包,它执行m_hasEvents.load(std::memory_order_relaxed);并返回值false.

    线程A被调度程序中断,线程B开始运行.

    线程B将事件推入队列并存储到 s_hasEvent

    线程B运行s_cv.notify_one().

    线程B被调度程序中断,线程A再次运行.

    线程A评估false闭包返回的结果,确定没有挂起事件.

    线程A条件变量上的块,等待事件.

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

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


是。需要互斥体以防止与条件变量发生竞争。它与条件本身无关,也与它的原子性无关。
推荐阅读
黄晓敏3023
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有