Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

std::chrono库是C++11引入的用于处理时间和日期的高级库。其核心是时钟(Clocks),每个时钟都是一个类型,提供了不同的时间点和时间间隔的度量方式。

C++标准定义了至少三种时钟类型,不同实现(如GCC、Clang、MSVC)可能会提供更多。本文将重点讲解标准要求的三种。

1. 核心概念

在介绍具体时钟前,先理解每个时钟都必须提供的两个核心成员:

  1. **now()**:一个静态成员函数,用于获取当前时间点(time_point)。例如:auto current_time = std::chrono::system_clock::now();
  2. **period**:一个类型定义(typedef),表示时钟的“滴答周期”(tick period),即时钟计时一次所代表的时间长度(通常是std::ratio类型)。例如,std::ratio<1, 1000000>表示每tick是1微秒。
  3. **duration**:一个类型定义,表示时钟使用的时间间隔类型,通常是std::chrono::duration<rep, period>rep是算术类型(如long long),period就是上面的滴答周期。
  4. **time_point**:一个类型定义,表示时钟的时间点类型,通常是std::chrono::time_point<Clock, Duration>
  5. is_steady:一个静态布尔常量,指示时钟是否是稳定的(steady)。这是选择时钟的关键属性。
    • 稳定时钟:意味着时钟的速率是恒定且单调递增的,永远不会调整(例如,不会因为网络时间协议NTP或夏令时而回拨或跳变)。它只适合测量时间间隔。
    • 非稳定时钟:意味着时钟可能会被外部因素调整,适合表示“墙上的钟表”时间(wall time),但不适合精确测量流逝的时间。

2. std::chrono::system_clock

2.1. 功能与含义

  • 系统时钟,它表示操作系统维护的“实时时钟”(Wall Clock)。
  • 它指示的是从某个历史时间点(纪元,epoch)到现在所经过的时间。这个纪元通常是1970年1月1日00:00:00 UTC,也就是Unix时间戳(Unix Time)的纪元。
  • 不是稳定时钟is_steady通常为false)。系统时间可以被系统管理员、网络时间同步(NTP)或夏令时(DST)修改。这意味着两次调用now()得到的时间点,后面的可能比前面的值小。

2.2. 主要用途

  1. 获取当前日历时间(日期和时间):这是它的主要用途。
  2. 与时间点/时间戳相互转换:因为它通常基于Unix纪元,所以可以很容易地转换为time_t,进而与C库函数(如gmtime, strftime)或与其他系统交互。
  3. 记录文件的创建/修改时间、日志时间戳等需要具有人类可读意义的场景。

2.3. 代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <chrono>
#include <ctime>

int main() {
// 获取当前系统时间
auto now = std::chrono::system_clock::now();

// 转换为time_t (C风格的时间)
std::time_t now_time_t = std::chrono::system_clock::to_time_t(now);

// 转换为本地日历时间字符串
std::cout << "Current time: " << std::ctime(&now_time_t);

// 也可以转换为UTC时间
std::cout << "UTC time: " << std::asctime(std::gmtime(&now_time_t));

// 计算从纪元开始到现在过了多少秒
auto duration_since_epoch = now.time_since_epoch();
auto sec_since_epoch = std::chrono::duration_cast<std::chrono::seconds>(duration_since_epoch).count();
std::cout << "Seconds since epoch: " << sec_since_epoch << std::endl;

return 0;
}

3. std::chrono::steady_clock

3.1. 功能与含义

  • 稳定时钟,专门为测量时间间隔而设计。
  • 它是稳定的is_steadytrue)。它的时间点值保证是单调递增的,不受任何系统时间调整的影响。即使系统时间被往后调了1小时,steady_clock仍然会按照物理时间稳定地向前走。
  • 它的纪元(起始点)是任意的、由具体的实现定义的(通常是程序启动或系统启动时作为时间0点)。你不能将它转换为日历时间。它的唯一用途就是测量时间间隔

3.2. 主要用途

  1. 性能基准测试(Benchmarking):测量代码段的执行时间。
  2. 超时处理:在网络编程或并发编程中计算等待/超时时间。
  3. 动画/游戏循环:计算帧与帧之间的时间差(delta time)。
  4. 任何需要精确、可靠的时间间隔测量的场景

3.3. 代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <chrono>
#include <thread>

int main() {
// 记录开始时间点
auto start = std::chrono::steady_clock::now();

// 模拟一些工作(例如睡眠500毫秒)
std::this_thread::sleep_for(std::chrono::milliseconds(500));

// 记录结束时间点
auto end = std::chrono::steady_clock::now();

// 计算时间间隔
auto elapsed = end - start;

// 将时间间隔转换为毫秒并输出
auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed).count();
std::cout << "Elapsed time: " << elapsed_ms << " ms\n";

return 0;
}

4. std::chrono::high_resolution_clock

4.1. 功能与含义

  • 高分辨率时钟,它旨在提供当前系统所能提供的最小tick周期的时钟,即最高精度的时钟。
  • 它是具体实现定义的通常std::chrono::steady_clockstd::chrono::system_clock的别名。这意味着:
    • 它的is_steady属性取决于它到底是哪种时钟的别名。
    • 它的稳定性没有保证。在有的平台上它是稳定的,在有的平台上它不是。

4.2. 主要用途

  • 需要最高可能精度的时间点测量的场景。
  • 注意:由于其稳定性不确定,除非你非常清楚你的编译平台上的具体实现(并且确认它是稳定的),否则优先使用std::chrono::steady_clock来测量时间间隔会更安全、更可移植。high_resolution_clock的别名特性有时被称为“时钟的幻影”(a clock alias of last resort)。

4.3. 代码示例

1
2
3
4
5
// 用法与steady_clock类似,但要注意其稳定性
auto start = std::chrono::high_resolution_clock::now();
// ... 做一些操作
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(end - start).count();

5. (非标准但常见)std::chrono::utc_clockstd::chrono::tai_clock (C++20)

从C++20开始,标准库引入了更多用于处理时区和闰秒的时钟,但这些还不是所有编译器都完全支持的。

  • **utc_clock**:表示协调世界时(UTC),它会考虑闰秒。
  • **tai_clock**:表示国际原子时(TAI),它不考虑闰秒,是一个纯粹基于原子钟的连续时间尺度。

6. 总结与选择指南

特性 system_clock steady_clock high_resolution_clock
用途 获取日历时间 测量时间间隔 测量高精度时间间隔
稳定性 (可调整) (单调递增) 具体实现定义(可能稳定也可能不稳定)
纪元 通常为Unix纪元 (1970) 任意(程序/系统启动) 具体实现定义
可转换日历 (通过to_time_t
推荐场景 记录时间戳、与外部系统交互 性能测试、超时、游戏循环 需要最高精度,且不关心可移植性

6.1. 黄金法则

  • 想要“现在几点?”(日历/日期/时间) -> 用 system_clock
  • 想要“这段代码跑了多久?”(时间间隔) -> **优先使用steady_clock**(最安全可靠)
  • 除非有极端精度需求且了解当前平台具体实现,否则慎用high_resolution_clock

评论