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
возвращается до истечения запрошенного тайм-аута. И когда это произойдет:
Возвращаемое значение
- 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;
}
}
}