菜鸟笔记
提升您的技术认知

深入理解std::chrono的时钟Clock

std::chrono是C++11引入的日期时间处理库,其中包含3种时钟:system_clock,steady_clock,high_resolution_clock。近来需要使用高精度时间,很自然想到使用high_resolution_clock,然而使用后发现并非预期的得到自1970/1/1零点之后的计数,而是一个小得多的数字。那么这三种时钟有什么区别,用在什么情况下,我们来一探究竟。

问题

auto tp = std::chrono::high_resolution_clock::now();
std::cout << tp.time_since_epoch().count() << std::endl;

上述代码输出一个比较小的数字,high_resolution_clock的精度是纳秒,不可能是这么小的数字。

三种时钟的区别

所谓时钟,是指从一个时点开始,按照某个刻度的一个计数。如下代码摘自VC2017。

  • system_clock
struct system_clock
{	// wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTime
typedef long long rep;
typedef ratio_multiply<ratio<_XTIME_NSECS_PER_TICK, 1>, nano> period;
typedef chrono::duration<rep, period> duration;
typedef chrono::time_point<system_clock> time_point;
static constexpr bool is_steady = false;

对于system_clock,其起点是epoch,即1970-01-01 00:00:00 UTC,其刻度是1个tick,也就是_XTIME_NSECS_PER_TICK纳秒。

  • high_resolution_clock
typedef steady_clock high_resolution_clock;

high_resolution_clock实际上和steady_clock一样。

  • steady_clock
struct steady_clock
{	// wraps QueryPerformanceCounter
typedef long long rep;
typedef nano period;
typedef nanoseconds duration;
typedef chrono::time_point<steady_clock> time_point;
static constexpr bool is_steady = true;

steady_clock的刻度是1纳秒,起点并非1970-01-01 00:00:00 UTC,一般是系统启动时间,这就是问题的关键。steady_clock的作用是为了得到不随系统时间修改而变化的时间间隔,所以凡是想得到绝对时点的用法都是错误的。steady_clock是没有to_time_t()的实现的,而system_clock是有的。

三种时钟用在什么时候

  • system_clock:用在需要得到绝对时点的场景
auto tp = std::chrono::system_clock::now();
std::time_t tt = std::chrono::system_clock::to_time_t(tp);
std::cout << tt << "seconds from 1970-01-01 00:00:00 UTC" << std::endl;
  • steady_clock:用在需要得到时间间隔,并且这个时间间隔不会因为修改系统时间而受影响的场景
auto tp1 = std::chrono::steady_clock::now();
//do something
auto tp2 = std::chrono::steady_clock::now();
std::cout << std::chrono::duration_cast<std::chrono::microseconds>(tp2 - tp1).count() << "microseconds" << std::endl;
  • high_resolution_clock:high_resolution_clock是system_clock或steady_clock之一,根据情况使用

常见的错误用法

  • std::this_thread::sleep_until传入的是steady_clock::time_point
  • 根据steady_clock::time_point得到time_t