【问题标题】:Why do I need to acquire a lock to modify a shared "atomic" variable before notifying condition_variable [duplicate]为什么在通知 condition_variable 之前我需要获取锁来修改共享的“原子”变量
【发布时间】:2017-06-11 13:21:11
【问题描述】:

编码为cppreference.com:

打算修改变量的线程必须

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

即使共享变量是原子的,也必须在互斥体下进行修改,才能正确地将修改发布到等待线程。

我不太明白,为什么修改原子变量需要锁。请看以下代码sn-p:

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

在 PushEvent 函数中,我没有获取 s_mtx,因为 s_hasEvent 是一个原子变量,并且队列是无锁的。没有获取 s_mtx 锁有什么问题?

【问题讨论】:

  • 条件变量是共享状态,因此必须用互斥锁保护。
  • 问题是关于通过互斥锁(也是共享状态)而不是 condvar 来保护s_hasEvents。发布者不修改 condvar,它只调用notify_one(),这在互斥锁锁定时不需要这样做。
  • @JonathanWakely 答案应该被接受

标签: c++ c++11 condition-variable


【解决方案1】:

找到了关于这个问题的一个很好的解释in another thread。在

关于比赛条件的以下问题。

如果正在传送的数据是原子的,我们不能在“发送”端没有互斥锁吗?

在最后。

【讨论】:

    【解决方案2】:

    正如Yakk's answer to the question you linked to 中所指出的,它是为了防止这种导致错过唤醒的事件序列:

      1. 线程 A 锁定互斥体。
      1. 线程 A 调用执行 m_hasEvents.load(std::memory_order_relaxed); 的 lambda 闭包并返回值 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() 呼叫会唤醒它)。

    【讨论】:

    • 是的。需要互斥锁来防止与条件变量的竞争。它与条件本身无关,也与它的原子性无关。
    猜你喜欢
    • 2018-02-15
    • 2021-07-06
    • 1970-01-01
    • 2020-10-14
    • 1970-01-01
    • 2018-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多