为什么这段代码中的condition_variable会阻塞?

Why condition_variable in this code blocks?

本文关键字:variable condition 段代码 代码 为什么      更新时间:2023-10-16

我是条件变量的新手,我想知道为什么计数器变量后的这段代码块等于99?删除for循环并将"counter += 99"改为使代码工作,它与sleep_for有任何关系吗?谢谢你的帮助:)

#include<thread>
#include<condition_variable>
#include<mutex>
#include<chrono>
#include <iostream>
std::condition_variable cv;
std::mutex mtx;
int counter = 0;
void foo() {
    std:: unique_lock<std::mutex>lck{ mtx };
    //counter += 99;
    for (; counter < 100; counter++) {
        std::cout << counter << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
    }
    lck.unlock();
    cv.notify_one();
}
int main() {
    std::thread th(&foo);
    {
        std::unique_lock<std::mutex>lck{ mtx };
        cv.wait(lck, [] {
            return counter == 99;
        });
    }
    std::cout << "!!!!!" << std::endl;
    th.join();
}

让我们在代码中添加一些注释:

void foo() {
    std:: unique_lock<std::mutex>lck{ mtx };
    // counter == 0
    for (; counter < 100; counter++) {
        ...
    }
    // counter == 100
    lck.unlock();
    cv.notify_one(); // <== notify here
}

我们循环到counter == 100,此时我们通知cv。然而,我们正在等待counter == 99,当它得到通知时,这不是真的。wait()在代码中返回的唯一方法是在循环的最后一次迭代时发生虚假唤醒。

也许你想循环,而counter < 99

#include<thread>
#include<condition_variable>
#include<mutex>
#include<chrono>
#include <iostream>
std::condition_variable cv;
std::mutex mtx;
int counter = 0;
void foo() {
    std:: unique_lock<std::mutex>lck{ mtx };
    //counter += 99;
    for (; counter < 100; counter++) {                                // B
        std::cout << counter << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(20));
    }
    lck.unlock();
    cv.notify_one();                                                  // C
}

在循环结束时(B), counter的值为100。

int main() {
    std::thread th(&foo);
    {
        std::unique_lock<std::mutex>lck{ mtx };
        cv.wait(lck, [] {
            return counter == 99;                                     // A
        });
    }
    std::cout << "!!!!!" << std::endl;
    th.join();
}

唤醒运行main()的线程的条件是counter的值为99 ( a )。

因为在循环结束后调用unlock() (C),所以counter的值为100。当counter的值为99 (A)时,std::condition_variable才会唤醒它的等待线程。因此,除非在counter的值为99时,等待线程中有一个虚假的唤醒(这会导致std::condition_variable的条件计算为true),否则您的等待线程将永远等待。

您可以通过简单地更改std::condition_variable的谓词来修复此问题,如下所示:

    {
        std::unique_lock<std::mutex>lck{ mtx };
        cv.wait(lck, [] {
            return counter == 100; // CHANGED TO 100 FROM 99.
        });
    }

或者(排他类型)通过改变for()循环中的条件,如下所示:

for (; counter < 99; counter++) { // `counter < 100` is now `counter < 99`
    std::cout << counter << std::endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
}