Если часы ходят с постоянной частотой (вне зависимости от того, совпадает эта частота с 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
какого-то другого класса часов. Все эти классы определены в заголовке наряду с прочими средствами работы со временем.
Чуть ниже мы рассмотрим представления моментов времени, но сначала познакомимся с представлением интервалов.
4.3.2. Временные интервалы
Интервалы — самая простая часть подсистемы поддержки времени; они представлены шаблонным классом std::chrono::duration<>
(все имеющиеся в С++ средства работы со временем, которые используются в библиотеке Thread Library, находятся в пространстве имен std::chrono
). Первый параметр шаблона — это тип представления ( int
, long
или double
), второй — дробь, показывающая, сколько секунд представляет один интервал. Например, число минут, хранящееся в значении типа short
, равно std::chrono::duration<60,1>>
, потому что в одной минуте 60 секунд. С другой стороны, число миллисекунд, хранящееся в значении типа double
, равно std::chrono::duration<1, 1000>>
, потому что миллисекунда — это 1/1000 секунды.
В пространстве имен std::chrono
имеется набор предопределенных typedef
'ов для различных интервалов: nanoseconds
, microseconds
, milliseconds
, seconds
, minutes
и hours
. В них используется достаточно широкий целочисленный тип, подобранный так, чтобы можно было представить в выбранных единицах интервал продолжительностью свыше 500 лет. Имеются также typedef
для всех определенных в системе СИ степеней 10 — от std::atto
(10 -18) до std::exa
(10 18) (и более, если платформа поддерживает 128-разрядные целые числа) — чтобы можно было определить нестандартные интервалы, например std::duration
(число сотых долей секунды, хранящееся в значении типа double
).
Между типами интервалов существует неявное преобразование, если не требуется отсечение (то есть неявно преобразовать часы в секунды можно, а секунды в часы нельзя). Для явного преобразования предназначен шаблон функции std::chrono::duration_cast<>
:
std::chrono::milliseconds ms(54802);
std::chrono::seconds s =
std::chrono::duration_cast(ms);
Результат отсекается, а не округляется, поэтому в данном примере s
будет равно 54.
Для интервалов определены арифметические операции, то есть сложение и вычитание интервалов, а также умножение и деление на константу базового для представления типа (первый параметр шаблона) дает новый интервал. Таким образом, 5*seconds(1)
— то же самое, что seconds(5)
или minutes(1) - seconds(55)
. Количество единиц в интервале возвращает функция-член count()
. Так, std::chrono::milliseconds(1234).count()
равно 1234.
Чтобы задать ожидание в течение интервала времени, используется функция std::chrono::duration<>
. Вот, например, как задается ожидание готовности будущего результата в течение 35 миллисекунд:
std::future f = std::async(some_task);
if (f.wait_for(std::chrono::milliseconds(35)) ==
std::future_status::ready)
do_something_with(f.get());
Все функции ожидания возвращают код, показывающий, истек ли таймаут или произошло ожидаемое событие. В примере выше мы ожидаем будущий результат, поэтому функция вернет std::future_status::timeout
, если истек таймаут, std::future_status::ready
— если результат готов, и std::future_status::deferred
— если будущая задача отложена. Время ожидания измеряется с помощью библиотечного класса стабильных часов, поэтому 35 мс — это всегда 35 мс, даже если системные часы были подведены (вперёд или назад) в процессе ожидания. Разумеется, из-за особенностей системного планировщика и варьирующейся точности часов ОС фактическое время между вызовом функции в потоке и возвратом из нее может оказаться значительно больше 35 мс.
Читать дальше