std::condition_variable 为什么需要 std::mutex

std::condition_variable why does it need a std::mutex

我不确定我是否真的理解为什么std::condition_variable需要额外的std::mutex作为参数?它不应该自己锁定吗?

#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>

std::condition_variable cv;
std::mutex cv_m;
int i = 0;
bool done = false;

void waits()
{
    std::unique_lock<std::mutex> lk(cv_m);
    std::cout << "Waiting... \n";
    cv.wait(lk, []{return i == 1;});
    std::cout << "...finished waiting. i == 1\n";
    done = true;
}

void signals()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Notifying falsely...\n";
    cv.notify_one(); // waiting thread is notified with i == 0.
                     // cv.wait wakes up, checks i, and goes back to waiting

    std::unique_lock<std::mutex> lk(cv_m);
    i = 1;
    while (!done) 
    {
        std::cout << "Notifying true change...\n";
        lk.unlock();
        cv.notify_one(); // waiting thread is notified with i == 1, cv.wait returns
        std::this_thread::sleep_for(std::chrono::seconds(1));
        lk.lock();
    }
}

int main()
{
    std::thread t1(waits), t2(signals);
    t1.join(); 
    t2.join();
}

其次,在示例中,他们首先解锁互斥量(signals 方法)。他们为什么要这样做?他们不应该先锁定然后通知后解锁吗?

在使用多线程时要记住的一个很好的经验法则是,当你问一个问题时,结果可能是一个谎言。也就是说,答案在给你之后可能已经改变了。可靠地提出问题的唯一方法是使其有效地成为单线程。输入互斥量。

条件变量等待触发器以便检查其条件。要检查它的状态,它需要提出一个问题。

如果wait之前没有加锁,那么有可能你问了问题,拿到了条件,却被告知条件为false。当触发发生并且条件变为真时,这就变成了谎言。但是你不知道这一点,因为没有互斥锁使它成为有效的单线程。

相反,您在条件变量上等待永远不会触发的触发器,因为它已经触发了。这会陷入僵局。

互斥量保护谓词,也就是你正在等待的东西。由于您正在等待的东西必然在线程之间共享,因此必须以某种方式对其进行保护。

在您上面的示例中,i == 1 是谓词。互斥锁保护 i.

退一步思考为什么我们需要条件变量可能会有所帮助。一个线程检测到某种状态阻止它向前推进,需要等待其他线程更改该状态。这种状态检测必须在互斥锁下进行,因为状态必须共享(否则,另一个线程如何更改该状态?)。

但是线程不能释放互斥量然后等待。如果在释放互斥量之后但在线程设法等待之前状态发生变化怎么办?所以你需要一个原子的“解锁并等待”操作。这正是条件变量提供的。

没有互斥体,他们会解锁什么?

在释放锁之前或之后是否向条件变量发出信号的选择是一个复杂的选择,对双方都有好处。一般来说,在持有锁的时候发信号会得到更好的性能。