std::promise<
std::map<SomeIndexType, SomeDataType, SomeComparator,
SomeAllocator>::iterator> p;
auto sf = p.get_future().share();
В данном случае для sf
выводится тип std::shared_future<std::map<SomeIndexType, SomeDataType, SomeComparator, SomeAllocator>::iterator>
, такое название произнести-то трудно. Если компаратор или распределитель изменятся, то вам нужно будет поменять лишь тип обещания, а тип будущего результата изменится автоматически.
Иногда требуется ограничить время ожидания события — либо потому что на время исполнения некоторого участка кода наложены жесткие ограничения, либо потому что поток может заняться другой полезной работой, если событие долго не возникает. Для этого во многих функциях ожидания имеются перегруженные варианты, позволяющие задать величину таймаута.
4.3. Ожидание с ограничением по времени
Все блокирующие вызовы, рассмотренные до сих пор, приостанавливали выполнение потока на неопределенно долгое время — до тех пор, пока не произойдёт ожидаемое событие. Часто это вполне приемлемого в некоторых случаях время ожидания желательно ограничить. Например, это может быть полезно, когда нужно отправить сообщение вида «Я еще жив» интерактивному пользователю или другому процессу или когда ожидание действительно необходимо прервать, потому что пользователь устал ждать и нажал Cancel.
Можно задать таймаут одного из двух видов: интервальный, когда требуется ждать в течение определённого промежутка времени (к примеру, 30 миллисекунд) или абсолютный, когда требуется ждать до наступления указанного момента (например, 17:30:15.045987023 UTC 30 ноября 2011 года). У большинства функций ожидания имеются оба варианта. Варианты, принимающие интервальный таймаут, оканчиваются словом _for
, а принимающие абсолютный таймаут — словом _until
.
Например, в классе std::condition_variable
есть по два перегруженных варианта функций-членов wait_for()
и wait_until()
, соответствующие двум вариантам wait()
— первый ждет поступления сигнала или истечения таймаута или ложного пробуждения, второй проверяет при пробуждении переданный предикат и возвращает управление, только если предикат равен true
(и условной переменной поступил сигнал) или истек таймаут.
Прежде чем переходить к детальному обсуждению функций с таймаутами, рассмотрим, как в С++ задается время, и начнем с часов.
4.3.1. Часы
С точки зрения стандартной библиотеки С++, часы — это источник сведений о времени. Точнее, класс часов должен предоставлять четыре элемента информации:
• текущее время now;
• тип значения для представления времени, полученного от часов;
• величина такта часов;
• признак равномерного хода времени, такие часы называются стабильными.
Получить от часов текущее время можно с помощью статической функции-члена now()
; например, функция std::chrono::system_clock::now()
возвращает текущее время по системным часам. Тип точки во времени для конкретного класса часов определяется с помощью члена typedef time_point
, поэтому значение, возвращаемое функцией some_clock::now()
имеет тип some_clock::time_point
.
Тактовый период часов задается в виде числа долей секунды, которое определяется членом класса typedef period
; например, если часы тикают 25 раз в секунду, то член period
будет определён как std::ratio<1, 25>
, тогда как в часах, тикающих один раз в 2,5 секунды, член period
определён как std::ratio<5, 2>
. Если тактовый период не известен до начала выполнения программы или может изменяться во время работы, то period
можно определить как средний период, наименьший период или любое другое значение, которое сочтет нужным автор библиотеки. Нет гарантии, что тактовый период, наблюдаемый в любом конкретном прогоне программы, соответствует периоду, определённому с помощью члена period.
Если часы ходят с постоянной частотой (вне зависимости от того, совпадает эта частота с period
или нет) и не допускают подведения, то говорят, что часы стабильны. Статический член is_steady
класса часов равен true
, если часы стабильны, и false
в противном случае. Как правило, часы std::chrono::system_clock
нестабильны, потому что их можно подвести, даже если такое подведение производится автоматически, чтобы учесть локальный дрейф. Из-за подведения более позднее обращение к now()
может вернуть значение, меньшее, чем более раннее, а это нарушение требования к равномерному ходу часов. Как мы скоро увидим, стабильность важна для вычислений с таймаутами, поэтому в стандартной библиотеке С++ имеется класс стабильных часов — std::chrono::steady_clock
. Помимо него, стандартная библиотека содержит класс std::chrono::system_clock
(уже упоминавшийся выше), который представляет системный генератор «реального времени» и имеет функции для преобразования моментов времени в тип time_t
и обратно, и класс std::chrono::high_resolution_clock
, который представляет наименьший возможный тактовый период (и, следовательно, максимально возможное разрешение). Может статься, что этот тип на самом деле является псевдонимом typedef
какого-то другого класса часов. Все эти классы определены в заголовке <chrono>
наряду с прочими средствами работы со временем.