Spec-Zone.ru › C++

std::condition_variable_any::wait_until

template< class Lock, class Clock, class Duration >
std::cv_status 
    wait_until( Lock& lock,
                const std::chrono::time_point<Clock, Duration>& timeout_time );
(1) (с C++11)
template< class Lock, class Clock, class Duration, class Predicate >
bool wait_until( Lock& lock,
                 const std::chrono::time_point<Clock, Duration>& timeout_time,
                 Predicate stop_waiting );
(2) (с C++11)
template< class Lock, class Clock, class Duration, class Predicate >
bool wait_until( Lock& lock,
                 std::stop_token stoken,
                 const std::chrono::time_point<Clock, Duration>& timeout_time,
                 Predicate stop_waiting );
(3) (с C++20)

wait_until заставляет текущий поток заблокироваться до тех пор, пока не произойдет уведомление об изменении состояния переменной, пока не будет достигнуто определенное время или не произойдет ложное пробуждение, при необходимости циклически повторяя ожидание, пока не будет удовлетворено какое-либо условие (bool(stop_waiting()) == true).

1) Атомарно освобождает lock, блокирует текущий выполняемый поток и добавляет его в список потоков, ожидающих уведомления *this. Поток будет разблокирован, когда будет выполнено notify_all() или notify_one(), или когда будет достигнута абсолютная временная метка timeout_time. Также может быть разблокирован в результате ложного пробуждения. При разблокировке, независимо от причины, lock повторно приобретается и wait_until завершается.
2) Эквивалентно
while (!stop_waiting())
{
    if (wait_until(lock, timeout_time) == std::cv_status::timeout)
        return stop_waiting();
}
return true;
Этот перегруз может быть использован для игнорирования ложных пробуждений.
3) Перерыв ожидания: регистрирует condition_variable_any на период wait_until(), чтобы получить уведомление, если будет запрошено прекращение работы в заданном stoken состоянии; затем она эквивалентна
while (!stoken.stop_requested())
{
    if (stop_waiting())
        return true;
    if (wait_until(lock, timeout_time) == std::cv_status::timeout)
        return stop_waiting();
}
return stop_waiting();

Если эти функции не выполняют пост условие (lock заблокирован вызывающим потоком), вызывается std::terminate. Например, это может произойти, если повторное блокирование мьютекса выбросит исключение.

Clock должен соответствовать требованиям Clock. Программа является некорректной, если std::chrono::is_clock_v<Clock> false.(с C++20)

Параметры

lock - объект типа Lock, удовлетворяющий требованиям BasicLockable, который должен быть заблокирован текущим потоком
stoken - std::stop_token для регистрации прерывания
timeout_time - объект типа std::chrono::time_point, представляющий время, когда следует остановить ожидание
stop_waiting - предикат, возвращающий ​false если ожидание должно быть продолжено (bool(stop_waiting()) == false).

Подпись предикатной функции должна быть эквивалентна следующей:

bool pred();​

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

1) std::cv_status::timeout если абсолютный таймаут, заданный timeout_time, был достигнут, std::cv_status::no_timeout в противном случае.
2) false если предикат stop_waiting по-прежнему возвращает false после истечения таймаута timeout_time, в противном случае true. Если таймаут истек, вычисляет и возвращает результат stop_waiting.
3) stop_waiting(), независимо от того, был ли достигнут таймаут или был запрошен останов.

Исключения

1) Любое исключение, сгенерированное часами, временной точкой или продолжительностью во время выполнения (часы, временные точки и продолжительности, предоставляемые стандартной библиотекой, никогда не выбрасывают исключения).
2) То же, что и (1), но также может передавать исключения, выброшенные stop_waiting.
3) То же, что и (2).

Примечания

Стандарт рекомендует использовать часы, связанные с timeout_time, для измерения времени; эти часы не обязательно должны быть монотонными. Нет гарантий относительно поведения этой функции, если часы изменяются дискретно, но существующие реализации преобразуют timeout_time из Clock в std::chrono::system_clock и делегируют вызов к POSIX pthread_cond_timedwait, чтобы ожидание учитывало изменения системных часов, но не предоставленных пользователем Clock. В любом случае, функция также может ждать дольше, чем до момента, когда timeout_time будет достигнут, из-за задержек планирования или конкуренции за ресурсы.

Даже если используемые часы являются std::chrono::steady_clock или другими монотонными часами, корректировка системных часов может вызвать ложное пробуждение.

Влияние notify_one()/notify_all() и каждой из трёх атомарных частей wait()/wait_for()/wait_until() (разблокировка + ожидание, пробуждение и блокировка) происходит в едином полном порядке, который можно рассматривать как порядок модификаций атомарной переменной: порядок специфичен для данной переменной состояния. Это делает невозможным, например, задержку notify_one() и разблокирование потока, который начал ожидание сразу после вызова notify_one().

Пример

#include <atomic>
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <thread>
using namespace std::chrono_literals;
 
std::condition_variable cv;
std::mutex cv_m;
std::atomic<int> i{0};
 
void waits(int idx)
{
    std::unique_lock<std::mutex> lk(cv_m);
    auto now = std::chrono::system_clock::now();
    if (cv.wait_until(lk, now + idx*100ms, [](){ return i == 1; }))
        std::cerr << "Thread " << idx << " finished waiting. i == " << i << '\n';
    else
        std::cerr << "Thread " << idx << " timed out. i == " << i << '\n';
}
 
void signals()
{
    std::this_thread::sleep_for(120ms);
    std::cerr << "Notifying...\n";
    cv.notify_all();
    std::this_thread::sleep_for(100ms);
    i = 1;
    std::cerr << "Notifying again...\n";
    cv.notify_all();
}
 
int main()
{
    std::thread t1(waits, 1), t2(waits, 2), t3(waits, 3), t4(signals);
    t1.join(); 
    t2.join();
    t3.join();
    t4.join();
}

Возможный вывод:

Thread 1 timed out. i == 0
Notifying...
Thread 2 timed out. i == 0
Notifying again...
Thread 3 finished waiting. i == 1

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.

DR Применён к Поведение, как опубликовано Правильное поведение
LWG 2093 C++11 исключения, связанные с таймаутом, отсутствовали в спецификации упомянуты
LWG 2135 C++11 wait_until вызывала исключение при разблокировке/переблокировке вызывает std::terminate

См. также

wait
блокирует текущий поток до пробуждения переменной состояния
(публичный член-функция)
wait_for
блокирует текущий поток до пробуждения переменной состояния или после истечения указанного времени ожидания
(публичный член-функция)
Документация на C для cnd_timedwait

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/thread/condition_variable_any/wait_until

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API