Spec-Zone.ru › C++

std::condition_variable::wait_for

template< class Rep, class Period >
std::cv_status wait_for( std::unique_lock<std::mutex>& lock,
                         const std::chrono::duration<Rep, Period>& rel_time );
(1) (с C++11)
template< class Rep, class Period, class Predicate >
bool wait_for( std::unique_lock<std::mutex>& lock,
               const std::chrono::duration<Rep, Period>& rel_time,
               Predicate stop_waiting );
(2) (с C++11)
1) Атомарно освобождает lock, блокирует текущий поток выполнения и добавляет его в список потоков, ожидающих *this. Поток будет разблокирован, когда будет выполнено notify_all() или notify_one(), или когда истечёт заданный относительный таймаут rel_time. Он также может быть разблокирован случайным образом. При разблокировании, независимо от причины, lock повторно приобретается, и выполнение wait_for() завершается.
2) Эквивалентно return wait_until(lock, std::chrono::steady_clock::now() + rel_time, std::move(stop_waiting));. Этот перегруз может использоваться для игнорирования ложных пробуждений, циклически проверяя, пока не будет выполнено некоторое условие (bool(stop_waiting()) == true).

Стандарт рекомендует использовать стабильный таймер для измерения продолжительности ожидания. Данная функция может блокироваться дольше, чем timeout_duration из-за задержек планирования или конкуренции за ресурсы.

Вызов этой функции, если lock.mutex() не заблокирован текущим потоком, приводит к неопределённому поведению.

Вызов этой функции, если lock.mutex() не является тем же мьютексом, что и тот, который используется всеми другими потоками, которые в данный момент ожидают того же условия, приводит к неопределённому поведению.

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

Параметры

lock - объект типа std::unique_lock<std::mutex>, который должен быть заблокирован текущим потоком
rel_time - объект типа std::chrono::duration, представляющий максимальное время ожидания. Обратите внимание, что rel_time должен быть достаточно малым, чтобы избежать переполнения при добавлении к std::chrono::steady_clock::now().
stop_waiting - предикат, возвращающий ​false если ожидание должно продолжаться (bool(stop_waiting()) == false).

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

bool pred();​

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

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

Исключения

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

Примечания

Даже если уведомление выполняется под блокировкой, перегрузка (1) не гарантирует состояние связанного предиката при возвращении из-за истечения таймаута.

Действия 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;
int i;
 
void waits(int idx)
{
    std::unique_lock<std::mutex> lk(cv_m);
    if (cv.wait_for(lk, 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);
    {
        std::lock_guard<std::mutex> lk(cv_m);
        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_for выбрасывало исключение при разблокировании/переблокировании с ошибкой вызывается std::terminate

См. также

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

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

Spec-Zone.ru

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