在这段代码中,wait() 调用是如何通过通知调用的?

问题描述

我有一个使用 condition-variable 进行同步的 C++ 代码,如下所示。

#include <iostream> 
#include <condition_variable>

int n = 4;
enum class Turn { FOO,BAR };
Turn turn = Turn::FOO;

std::mutex mut;
std::condition_variable cv;

void foo() {
    for (int i = 0; i < n; i++) {
        std::unique_lock<std::mutex> lock(mut);

        // wait for signal from bar & turn == FOO
        cv.wait(lock,[] {return turn == Turn::FOO; });

        std::cout << "foo" << std::endl;

        // unlock & signal bar
        lock.unlock();
        turn = Turn::BAR;
        cv.notify_one();
    }
}

void bar() {
    for (int i = 0; i < n; i++) {
        std::unique_lock<std::mutex> lock(mut);

        // wait for signal from foo & turn == BAR
        cv.wait(lock,[] {return turn == Turn::BAR; });

        std::cout << "bar" << std::endl;

        // unlock &  signal foo
        lock.unlock();
        turn = Turn::FOO;
        cv.notify_one();
    }
}

int main() {
    std::thread thread_1(foo);
    std::thread thread_2(bar);
    thread_2.join();
    thread_1.join();
    
    return 0; 
}

观察到的输出

Output

问题:

最初如何触发 cv.wait(lock,[] {return turn == Turn::FOO; }); 中的 foo()

从我读到的内容来看,带有谓词的 wait() 调用相当于:while (!pred()) { wait(lock); }。谓词一开始为真(turn 的初始值为Turn::FOO),但是wait 调用如何获得通知呢?关于wait(),我看到了:

原子地解锁锁,阻塞当前正在执行的线程,并将其添加到等待 *this 的线程列表中。当notify_all() 或notify_one() 被执行时,线程将被解除阻塞。它也可能被虚假地解除阻塞。解除阻塞后,无论何种原因,都会重新获取锁并等待退出

但我没有看到另一个线程(运行 bar() 的线程)执行了 notify_one(),因为 turn 仍然是 FOO

解决方法

foo() 中的 cv.wait 如何在开始时被触发?

它将由对 true 求值的谓词触发。等效循环:

while (!pred()) {
    wait(lock);
}

甚至一次都不会调用 wait()(无论如何都是第一次访问该行代码)。