Spec-Zone.ru › C++

std::condition_variable_any::wait

template< class Lock >
void wait( Lock& lock );
(1) (с C++11)
template< class Lock, class Predicate >
void wait( Lock& lock, Predicate stop_waiting );
(2) (с C++11)
template< class Lock, class Predicate >
bool wait( Lock& lock, std::stop_token stoken, Predicate stop_waiting );
(3) (с C++20)

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

1) Атомарно разблокирует lock, блокирует текущий поток выполнения и добавляет его в список потоков, ожидающих *this. Поток будет разблокирован, когда выполнится notify_all() или notify_one(). Он также может быть разблокирован ложно. При разблокировании, независимо от причины, lock снова приобретается, и wait завершается.
2) Эквивалентно
while (!stop_waiting())
{
    wait(lock);
}

Этот перегруз может быть использован для игнорирования ложных пробуждений при ожидании, пока определённое условие не станет истинным. Обратите внимание, что lock необходимо приобрести перед входом в этот метод, и он снова приобретается после выхода из wait(lock), что означает, что lock может использоваться для защиты доступа к stop_waiting().

3) Прерывимое ожидание: регистрирует condition_variable_any на время wait(), чтобы получить уведомление, если запрос остановки был сделан для данного stoken's связанного состояния остановки; тогда оно эквивалентно
while (!stoken.stop_requested())
{
    if (stop_waiting())
        return true;
    wait(lock);
}
return stop_waiting();
Обратите внимание, что возвращаемое значение указывает, был ли stop_waiting оценён как true, независимо от того, был ли запрос остановки или нет.

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

Параметры

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

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

bool pred();​

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

1,2) (ничего)
3) stop_waiting()

Исключения

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

Примечания

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

Пример

#include <chrono>
#include <condition_variable>
#include <iostream>
#include <thread>
 
std::condition_variable_any cv;
std::mutex cv_m; // This mutex is used for three purposes:
                 // 1) to synchronize accesses to i
                 // 2) to synchronize accesses to std::cerr
                 // 3) for the condition variable cv
int i = 0;
 
void waits()
{
    std::unique_lock<std::mutex> lk(cv_m);
    std::cerr << "Waiting... \n";
    cv.wait(lk, []{ return i == 1; });
    std::cerr << "...finished waiting. i == 1\n";
}
 
void signals()
{
    std::this_thread::sleep_for(std::chrono::seconds(1));
    {
        std::lock_guard<std::mutex> lk(cv_m);
        std::cerr << "Notifying...\n";
    }
    cv.notify_all();
 
    std::this_thread::sleep_for(std::chrono::seconds(1));
 
    {
        std::lock_guard<std::mutex> lk(cv_m);
        i = 1;
        std::cerr << "Notifying again...\n";
    }
    cv.notify_all();
}
 
int main()
{
    std::thread t1(waits), t2(waits), t3(waits), t4(signals);
    t1.join(); 
    t2.join(); 
    t3.join();
    t4.join();
}

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

Waiting...
Waiting...
Waiting...
Notifying...
Notifying again...
...finished waiting. i == 1
...finished waiting. i == 1
...finished waiting. i == 1

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

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

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

См. также

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

Внешние ссылки

1. Статья The Old New Thing: Ложные пробуждения в переменных состояния Win32.

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

Spec-Zone.ru

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