Это рекомендуемый способ ожидания условной переменной с ограничением по времени в случае, когда предикат не указывается. При этом ограничивается общее время выполнения цикла. В разделе 4.1.1 мы видели, что при использовании условных переменных без предиката цикл необходим для защиты от ложных пробуждений. Но если вызывать в цикле wait_for(), то может получиться, что функция прождёт почти все отведенное время, а затем произойдёт ложное пробуждение, после чего на следующей итерации отсчет времени начнется заново. И так может происходить сколько угодно раз, в результате чего общее время ожидания окажется неограниченным.
Вооружившись знаниями о том, как задавать таймауты, рассмотрим функции, в которых таймауты используются.
4.3.4. Функции, принимающие таймаут
Простейший случай использования таймаута — задание паузы в потоке, чтобы он не отнимал у других потоков время, когда ему нечего делать. Соответствующий пример был приведён в разделе 4.1, где мы в цикле опрашивали флаг «done». Для этого использовались функции std::this_thread::sleep_for() и std::this_thread::sleep_until(). Обе работают как будильник: поток засыпает либо на указанный интервал (в случае sleep_for()), либо до указанного момента времени (в случае sleep_until()). Функцию sleep_for() имеет смысл применять в ситуации, описанной в разделе 4.1, когда что-то необходимо делать периодически и важна лишь продолжительность периода. С другой стороны, функция sleep_until() позволяет запланировать пробуждение потока в конкретный момент времени, например: запустить в полночь резервное копирование, начать в 6 утра распечатку платёжной ведомости или приостановить поток до момента следующего обновления кадра при воспроизведении видео.
Разумеется, таймаут принимают не только функции типа sleep. Выше мы видели, что таймаут можно задавать при ожидании условных переменных и будущих результатов. А также при попытке захватить мьютекс, если сам мьютекс такую возможность поддерживает. Обычные классы std::mutex и std::recursive_mutex не поддерживают таймаут при захвате, зато его поддерживают классы std::timed_mutex и std::recursive_timed_mutex. В том и в другом имеются функции-члены try_lock_for() и try_lock_until(), которые пытаются получить блокировку в течение указанного интервала или до наступления указанного момента времени. В табл. 4.1 перечислены функции из стандартной библиотеки С++, которые принимают таймауты, их параметры и возвращаемые значения. Параметр должен быть объектом типа std::duration<>, а параметр — объектом типа std::time_point<>.
Таблица 4.1. Функции, принимающие таймаут