【问题标题】:std::condition_variable why does it need a std::mutexstd::condition_variable 为什么需要 std::mutex
【发布时间】:2016-09-07 21:53:18
【问题描述】:

我不确定我是否真的明白为什么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 方法)。他们为什么这样做?不是应该先锁,然后通知后解锁吗?

【问题讨论】:

    标签: c++11 condition-variable


    【解决方案1】:

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

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

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

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

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

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

    【讨论】:

    • 即使我使用std::atomic&lt;int&gt;,同样的解释是否成立?
    • @jeffreyveon 使整数原子化没有任何好处,因为无论如何您都必须将其包装在互斥体中(以避免在您决定阻塞条件变量后整数发生变化时出现竞争条件)。
    【解决方案2】:

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

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

    如果你在等待之前没有加锁,那么有可能你问了问题得到了条件,结果你被告知条件为假。当触发发生并且条件变为真时,这变成了谎言。但是你不知道这一点,因为没有互斥锁可以有效地实现单线程。

    相反,您在条件变量上等待一个永远不会触发的触发器,因为它已经触发了。这会死锁。

    【讨论】:

    • 好的,感谢您的解释。但是请问为什么这个不直接放到std::condition_variable的实现中呢?
    • 是的。这就是条件变量的等待函数将锁作为参数的原因!
    猜你喜欢
    • 2012-11-02
    • 2018-02-15
    • 2017-03-28
    • 2020-11-15
    • 1970-01-01
    • 2012-10-17
    • 1970-01-01
    • 2021-05-24
    • 1970-01-01
    相关资源
    最近更新 更多