condition_variable wait_for в C++

Я работаю с condition_variable на Visual Studio 2019. condition_variable.wait_for() функция возвращает std::cv_status::no_timeout без уведомления.

#include <iostream>
#include <thread>
#include <chrono>
#include <mutex>

std::condition_variable cv;
std::mutex mtx;
bool called = false;

void printThread()
{
    std::unique_lock<std::mutex> lck(mtx);
    while (std::cv_status::timeout == cv.wait_for(lck, std::chrono::seconds(1)))
    {
        std::cout << "*";
    }
    std::cout << "thread exits" << std::endl;
}

int main()
{
    std::thread th(printThread);
    th.join();
    std::cout << "program exits" << std::endl;
}

Я думаю, что код никогда не выйдет и продолжит печатать *, но он выходит после печати некоторых *.

Вот результат:

********************************************************************thread exits
program exits

Почему так происходит? Это так называемые "ложные пробуждения"?

2 ответа

Решение

Да, это "ложное пробуждение". Это объясняется на справочной странице cppreference.com для wait_for:

Он также может быть ложно разблокирован. При разблокировке, независимо от причины, блокировка восстанавливается и wait_for() завершает работу.

Перевод: в вашем компьютере есть гремлины. Иногда они становятся сварливыми. И если они станут сварливыми, wait_forвозвращается до истечения запрошенного тайм-аута. И когда это произойдет:

Возвращаемое значение

  1. std::cv_status::timeout, если относительное время ожидания, указанное rel_time, истекло, std::cv_status::no_timeout в противном случае.

И похоже, это именно то, что вы видите. Стандарт C++ позволяет реализации C++ возвращаться из wait_forпреждевременно, по произвольным причинам, и если вы не вернетесь из wait_for когда истечет время ожидания, no_timeout это то, что вы получаете.

Вам может быть интересно, почему wait_for(и несколько других подобных функций) могут решить вскинуть руки и вернуться "ложно". Но это был бы другой вопрос...

Как уже объяснялось, он просыпается из-за spurious wakeup. Такая вещь делает функцию wait_forсовершенно бесполезно. Решение состоит в том, чтобы использовать wait_until сохранение текущего времени перед входом в цикл ожидания:

      int count = 1;
std::mutex mutex;
std::condition_variable condition_variable;

void wait() {
    std::unique_lock<std::mutex> lock(mutex);
    count--;

    int timeout = 1000; // 1 second
    std::chrono::time_point<std::chrono::system_clock> timenow = 
        std::chrono::system_clock::now();

    while(count < 0) {
        std::cv_status status = condition_variable.wait_until(
            lock, 
            timenow + std::chrono::duration<double,std::ratio<1,1000>>(timeout));

        if ( std::cv_status::timeout == status) {
            count++;
            break;
        }
    }
}
Другие вопросы по тегам