std::chrono 时间库
定义与作用
<chrono> 是 C++11 引入的高精度时间库,通过三大核心组件——时长(duration)、时间点(time_point)、时钟(clock)——提供类型安全、单位明确的时间处理能力,全面替代 C 风格的 time() / clock() / gettimeofday()。
#include <chrono>
auto start = std::chrono::steady_clock::now();
// ... 执行任务 ...
auto end = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cout << "耗时: " << elapsed.count() << " ms" << std::endl;
| 对比 | C 风格 | <chrono> |
|---|---|---|
| 类型安全 | time_t 是整数,易混用 | duration 带单位,编译期检查 |
| 精度 | 秒级(time()) | 纳秒级可配 |
| 单调性 | time() 受系统时间调整影响 | steady_clock 保证单调递增 |
| 运算 | 手动单位换算 | duration_cast 自动换算 |
核心原理
三大组件关系
duration 的编译期单位体系
三种时钟对比
| 时钟 | 特点 | 适用场景 |
|---|---|---|
std::chrono::system_clock | 系统时钟,可转为 time_t 和日历时间 | 需要显示日期时间 |
std::chrono::steady_clock | 单调时钟,不受系统时间调整影响 | 性能测量、超时控制 |
std::chrono::high_resolution_clock | 最高精度(通常是 steady 或 system 的别名) | 对精度要求极高的测量 |
完整示例
示例一:飞翔科技服务性能测量
场景说明:李眉压测公司内部 API,使用 chrono 精确测量各接口延迟。
#include <iostream>
#include <chrono>
#include <thread>
#include <vector>
#include <string>
#include <iomanip>
#include <cmath>
// 模拟飞翔科技后端 API 处理
int processQuery(const std::string& apiName, int complexity) {
// 模拟不同复杂度的处理耗时
auto delay = std::chrono::milliseconds(10 + complexity * 5);
std::this_thread::sleep_for(delay);
return complexity * 42;
}
// 计时包装器:测量函数执行时间
template <typename F, typename... Args>
auto measureTime(const std::string& label, F&& func, Args&&... args) {
auto start = std::chrono::steady_clock::now();
auto result = std::forward<F>(func)(std::forward<Args>(args)...);
auto end = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
std::cout << " " << std::setw(20) << std::left << label
<< std::setw(8) << std::right << elapsed.count()
<< " μs";
// 当耗时 > 100ms 时自动转为 ms 显示
if (elapsed > std::chrono::milliseconds(100)) {
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(elapsed);
std::cout << " (" << ms.count() << " ms)";
}
std::cout << std::endl;
return result;
}
int main() {
std::cout << "===== 飞翔科技 API 性能测试 =====" << std::endl;
std::cout << "steady_clock 精度: 1/"
<< std::chrono::steady_clock::period::den
<< " 秒"
<< std::endl << std::endl;
// 测试多个 API
auto r1 = measureTime("用户登录",
processQuery, "/api/login", 5);
auto r2 = measureTime("订单查询",
processQuery, "/api/orders", 20);
auto r3 = measureTime("报表生成",
processQuery, "/api/report", 50);
// ---- duration 算术运算 ----
std::cout << "\n[duration 运算]" << std::endl;
using namespace std::chrono_literals; // C++14 字面量
auto task1 = 150ms; // 任务1预计用时
auto task2 = 300ms; // 任务2预计用时
auto total = task1 + task2;
std::cout << "task1 + task2 = "
<< std::chrono::duration_cast<std::chrono::milliseconds>(total).count()
<< " ms" << std::endl;
// 比较
if (task1 < task2) {
std::cout << "task1 比 task2 快" << std::endl;
}
return 0;
}
预期输出:
===== 飞翔科技 API 性能测试 =====
steady_clock 精度: 1/1000000000 秒
用户登录 35000 μs
订单查询 110000 μs (110 ms)
报表生成 260000 μs (260 ms)
[duration 运算]
task1 + task2 = 450 ms
task1 比 task2 快
逐段分析:
steady_clock::now()返回当前时间点——单调时钟不受用户修改系统时间的影响duration_cast<microseconds>(end - start)将时间点差值转换为微秒精度std::chrono_literals(C++14)提供150ms、3s、5min等字面量语法duration_cast是截断转换:duration_cast<milliseconds>(1500μs)= 1ms(小数部分丢失)steady_clock::period::den获取时钟的精度分母
示例二:超时控制与时间点比较
场景说明:白歌为飞翔科技消息队列设计带超时的 consumer。
#include <iostream>
#include <chrono>
#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
class TimeoutConsumer {
public:
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
// 等待消息,超时时间由 duration 指定
template <typename Rep, typename Period>
bool waitForMessage(const std::chrono::duration<Rep, Period>& timeout) {
auto deadline = Clock::now() + timeout;
while (Clock::now() < deadline) {
// 模拟检查是否有消息
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (hasNewMessage()) {
std::cout << " 收到消息: " << fetchMessage() << std::endl;
return true;
}
}
std::cout << " 超时!等待了 "
<< std::chrono::duration_cast<std::chrono::milliseconds>(timeout).count()
<< " ms 仍无消息" << std::endl;
return false;
}
void produce(const std::string& msg) {
std::this_thread::sleep_for(std::chrono::milliseconds(300));
lastMessage_ = msg;
hasMessage_ = true;
}
private:
bool hasNewMessage() const { return hasMessage_; }
std::string fetchMessage() {
hasMessage_ = false;
return lastMessage_;
}
std::string lastMessage_;
bool hasMessage_ = false;
};
int main() {
std::cout << "===== 飞翔科技消息队列超时 =====" << std::endl;
TimeoutConsumer consumer;
// 场景1:消息及时到达(300ms < 500ms 超时)
std::cout << "\n[场景1 — 正常接收]" << std::endl;
std::thread producer([&consumer]() {
consumer.produce("孔蓝: 需求文档已更新");
});
bool received = consumer.waitForMessage(std::chrono::milliseconds(500));
std::cout << "结果: " << (received ? "成功接收" : "超时") << std::endl;
producer.join();
// 场景2:消息未在超时前到达
std::cout << "\n[场景2 — 超时]" << std::endl;
received = consumer.waitForMessage(std::chrono::milliseconds(200));
std::cout << "结果: " << (received ? "成功接收" : "超时") << std::endl;
// ---- 时间点与 system_clock 转换 ----
std::cout << "\n[时间点与日历时间]" << std::endl;
auto now = std::chrono::system_clock::now();
std::time_t now_c = std::chrono::system_clock::to_time_t(now);
// 分解为时间结构(方便显示)
std::tm local_tm;
localtime_s(&local_tm, &now_c); // Windows
// localtime_r(&now_c, &local_tm); // POSIX
std::cout << "当前系统时间: "
<< (local_tm.tm_year + 1900) << "-"
<< std::setfill('0')
<< std::setw(2) << (local_tm.tm_mon + 1) << "-"
<< std::setw(2) << local_tm.tm_mday << " "
<< std::setw(2) << local_tm.tm_hour << ":"
<< std::setw(2) << local_tm.tm_min << ":"
<< std::setw(2) << local_tm.tm_sec
<< std::endl;
// 计算两个时间戳之间的天数差
auto yesterday = now - std::chrono::hours(24);
auto diff_hours = std::chrono::duration_cast<std::chrono::hours>(now - yesterday);
std::cout << "距离昨天此时: " << diff_hours.count() << " 小时" << std::endl;
return 0;
}
预期输出:
===== 飞翔科技消息队列超时 =====
[场景1 — 正常接收]
收到消息: 孔蓝: 需求文档已更新
结果: 成功接收
[场景2 — 超时]
超时!等待了 200 ms 仍无消息
结果: 超时
[时间点与日历时间]
当前系统时间: 2026-6-14 10:30:45
距离昨天此时: 24 小时
逐段分析:
- 超时模式:
deadline = now() + timeout,循环检查now() < deadline steady_clock用于超时控制:单调递增,不受系统时间调整影响——避免了因 NTP 校时导致超时时间计算混乱system_clock::to_time_t()将time_point转为 C 风格time_t,用于与旧 API 交互time_point之间相减得到duration,time_point加duration得到新time_point- 代码使用模板接受任意
duration,调用者可自由传入500ms、2s、1min等不同单位
示例三:C 风格时间函数 vs chrono 对比
#include <iostream>
#include <chrono>
#include <ctime>
#include <thread>
int main() {
std::cout << "===== C time() vs chrono =====" << std::endl;
// ---- C 风格 ----
std::cout << "\n[C 风格 — time_t]" << std::endl;
std::time_t start_c = std::time(nullptr);
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::time_t end_c = std::time(nullptr);
std::cout << "time() 差值: " << (end_c - start_c)
<< " 秒(精度限制在秒级)" << std::endl;
// ---- C 风格 clock()(CPU 时间,不是挂钟时间)----
std::cout << "\n[C 风格 — clock()]" << std::endl;
std::clock_t start_clk = std::clock();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::clock_t end_clk = std::clock();
std::cout << "clock() 差值: " << (end_clk - start_clk)
<< " ticks(sleep 不消耗 CPU,所以几乎为 0)" << std::endl;
// ---- chrono 风格 ----
std::cout << "\n[chrono 风格 — steady_clock]" << std::endl;
auto start_ch = std::chrono::steady_clock::now();
std::this_thread::sleep_for(std::chrono::milliseconds(500));
auto end_ch = std::chrono::steady_clock::now();
auto elapsed_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(end_ch - start_ch);
auto elapsed_us = std::chrono::duration_cast<std::chrono::microseconds>(end_ch - start_ch);
auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end_ch - start_ch);
std::cout << "chrono 测量 sleep(500ms) 实际耗时:" << std::endl;
std::cout << " " << elapsed_ns.count() << " ns" << std::endl;
std::cout << " " << elapsed_us.count() << " μs" << std::endl;
std::cout << " " << elapsed_ms.count() << " ms" << std::endl;
std::cout << "\n对比总结:" << std::endl;
std::cout << " time(): 秒级精度,测量挂钟时间" << std::endl;
std::cout << " clock(): 毫秒级精度,测量 CPU 时间(不是挂钟)" << std::endl;
std::cout << " chrono: 纳秒级精度,可选单调/系统/高精度时钟" << std::endl;
return 0;
}
预期输出:
===== C time() vs chrono =====
[C 风格 — time_t]
time() 差值: 0 秒(精度限制在秒级)
[C 风格 — clock()]
clock() 差值: 0 ticks(sleep 不消耗 CPU,所以几乎为 0)
[chrono 风格 — steady_clock]
chrono 测量 sleep(500ms) 实际耗时:
500123456 ns
500123 μs
500 ms
对比总结:
time(): 秒级精度,测量挂钟时间
clock(): 毫秒级精度,测量 CPU 时间(不是挂钟)
chrono: 纳秒级精度,可选单调/系统/高精度时钟
逐段分析:
time()精度仅到秒——500ms 的 sleep 差值几乎总是 0 或 1clock()测量的是 CPU 时间而非挂钟时间——sleep 不消耗 CPU,所以差值为 0chrono::steady_clock精确到纳秒,测量的是真实的挂钟时间duration_cast<不同精度级别>在不同精度单位间转换——高精度 → 低精度时小数部分被截断
易错场景与面试考点
易错场景
| 场景 | 错误表现 | 正确做法 |
|---|---|---|
用 system_clock 测量性能 | 系统时间调整导致测量不准 | 性能测量用 steady_clock |
duration_cast 精度丢失 | 截断而非四舍五入 | C++17 用 round/floor/ceil |
clock() 用于挂钟计时 | 测量的是 CPU 时间,非挂钟 | 用 steady_clock::now() |
忽略 steady_clock 是否可用 | 某些嵌入式平台不支持 | 检查文档,必要时回退 |
| duration 类型不匹配直接运算 | 编译错误 | 用 duration_cast 统一单位 |
常见面试问题
三种时钟的区别和选择?——
system_clock可转日历时间,受系统时间调整影响;steady_clock单调递增,适合性能测量;high_resolution_clock是编译器的最高精度时钟(通常是前两者之一的别名)。duration_cast的截断行为是什么?——向零取整(truncation toward zero)。duration_cast<seconds>(1500ms)= 1s。C++17 新增floor(向下取整)、ceil(向上取整)、round(四舍五入)。chrono 如何与 C 风格的
time_t互操作?——system_clock::to_time_t()将time_point转为time_t,system_clock::from_time_t()反向转换。为什么
clock()不能用于测量挂钟时间?——clock()返回的是进程消耗的 CPU 时间,sleep/IO 等待期间不消耗 CPU,因此clock()差值接近 0。
小结
- 三大核心:
duration(带单位的时长)、time_point(时刻)、clock(时钟源) - 性能测量用
steady_clock(单调递增,不受系统时间调整影响) - 显示日历时间用
system_clock(可转为time_t和本地时间) duration_cast用于单位转换(截断),C++17 起可用round/floor/ceilstd::chrono_literals(C++14)提供150ms、3s等字面量,代码更直观- 全面替代 C 风格的
time()(秒级)和clock()(CPU 时间)