跳到主要内容C++
C++ chrono 库详解:steady_clock 与 duration 原理及实战
介绍 C++11 chrono 库核心组件 steady_clock、time_point 和 duration。对比三种时钟差异,强调 steady_clock 适用于程序内计时。讲解时间点相减得时间间隔、时间点加间隔得新时间点的规则。提供耗时统计、固定频率调度、精准延迟、超时判断等实战案例。指出避免使用 Sleep、system_clock 及忽略.count() 方法的常见错误。
猫巷少女18K 浏览 前言
在 C++ 开发中,我们经常会遇到精准计时、频率控制、耗时统计的需求,比如:控制界面文字 1 秒刷新一次、统计函数执行耗时、实现固定频率的任务调度、游戏帧率控制等。
而 C++11 推出的 <chrono> 标准库,是处理时间相关需求的最优解,彻底替代了传统的 time()、clock() 等 C 语言老旧接口。其中 chrono::steady_clock::time_point(时间点)和 chrono::duration(时间间隔)是 <chrono> 库的两大核心组件,也是日常开发中使用频率最高的两个工具,二者组合可以完美实现所有高精度计时、定时需求。
本文将从原理剖析、语法详解、常用用法、实战案例、避坑指南等维度,把这两个核心知识点彻底讲透,示例代码均可直接编译运行。
一、chrono 库核心介绍
1.1 为什么要用 C++11 的 chrono 库?
在 C++11 之前,我们通常使用 C 语言的 <ctime> 库做计时,比如 time(NULL)、clock(),这类接口存在精度低(毫秒级甚至秒级)、接口混乱、跨平台兼容性差、容易出错等问题。
而 C++11 的 <chrono> 库的优势非常明显:
- 精度极高:支持纳秒 (ns)、微秒 (us)、毫秒 (ms)、秒 (s)、分钟 (min)、小时 (h) 等任意精度;
- 类型安全:时间点和时间间隔都是强类型,不会出现类型混用导致的逻辑错误;
- 跨平台兼容:标准库接口,Windows/Linux/Mac 全平台无差异使用;
- 语法优雅:面向对象封装,代码可读性高,易维护;
- 稳定可靠:提供专门的稳定时钟,计时过程中不受系统时间修改的影响。
1.2 chrono 库的三大核心组件
<chrono> 库的核心内容主要包含 3 个部分,三者相辅相成,我们今天的主角是前两个:
clock(时钟):时钟是产生「时间点」的源头,chrono 库提供了 3 种常用时钟,开发中 99% 的场景只用 steady_clock;
time_point(时间点):表示「某个具体的时刻」,比如「程序启动的那一刻」「当前系统时间」「上次执行任务的时刻」;
duration(时间间隔):表示「两个时间点之间的差值」,比如「从程序启动到现在过去了 3.5 秒」「两次任务执行间隔了 100 毫秒」。
二、核心组件 1:chrono::steady_clock 稳定时钟(重中之重)
2.1 为什么首选 steady_clock?
chrono 库提供了三种时钟,各自的适用场景完全不同,我们先明确结论:开发中做「程序内计时 / 定时」,推荐使用 steady_clock。
三种时钟的对比:
chrono::steady_clock 稳定时钟【推荐】
- 核心特性:时钟的时间只会单调递增,永远不会回拨、不会跳变,不受系统时间修改(比如手动改系统时间、同步网络时间)的任何影响。
- 精度:最高精度(通常是纳秒级),满足所有开发场景的精度需求。
- 适用场景:程序内的耗时统计、定时任务、频率控制(本文所有案例的核心时钟)。
chrono::system_clock 系统时钟
- 核心特性:与操作系统的「系统时间」绑定,能获取到当前的年月日时分秒,但是系统时间可以被手动修改,时钟可能会回拨。
适用场景:只适合「获取当前系统的日历时间」(比如打印日志时的当前时间),绝对不能用于程序内计时 / 定时。chrono::high_resolution_clock 高精度时钟
- 核心特性:理论上是系统能提供的「最高精度时钟」,但在不同编译器下的实现不同(比如 VS 中等价于 steady_clock,GCC 中等价于 system_clock),存在兼容性问题。
- 适用场景:无特殊高精度需求时,不用选择它,直接用 steady_clock 即可。
2.2 steady_clock 的核心使用语法
steady_clock是一个类,我们只需要用到它的一个静态成员方法和一个嵌套类型,语法固定,无需记忆,直接套用:
#include <chrono>
using namespace std;
chrono::steady_clock::time_point curr_time = chrono::steady_clock::now();
auto curr_time = chrono::steady_clock::now();
核心说明:chrono::steady_clock::now() 是 steady_clock 的核心方法,作用是 获取「此时此刻」的稳定时间点,这个时间点是从程序启动 / 系统开机开始计时的,是一个绝对的、不会变化的时间戳。
三、核心组件 2:chrono::steady_clock::time_point 时间点
3.1 什么是「时间点」?
chrono::steady_clock::time_point 简称 time_point,可以理解为:一个用来「记录某一个具体时刻」的变量类型。
打个通俗的比方:time_point就像我们手机上的「时间戳」,比如 2026-01-19 20:00:00.123 这个时刻,就是一个具体的时间点。在程序中,我们用 time_point 记录的是:从 steady_clock 的计时起点到「某个时刻」的总时长。
3.2 核心语法与定义方式
time_point 是 steady_clock 的嵌套类型,所以完整的类型名是 chrono::steady_clock::time_point,有两种定义方式,开发中一律推荐第二种:
#include <chrono>
using namespace std;
chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
auto t1 = chrono::steady_clock::now();
time_point 是一个只读的、不可修改的类型,只能通过 steady_clock::now() 获取,或者用来和其他 time_point 做差值计算;
time_point 本身不代表具体的「年月日时分秒」,只代表一个抽象的「时刻」,对开发者友好,无需关心底层的计时起点。
3.3 time_point 的核心操作(唯一常用操作)
time_point 类型的变量,日常开发中只有一个核心操作:做减法。
语法规则:两个 time_point 相减,得到的结果是一个 duration 类型的「时间间隔」
#include <chrono>
using namespace std;
auto t_start = chrono::steady_clock::now();
auto t_end = chrono::steady_clock::now();
auto time_diff = t_end - t_start;
这个语法是整个 chrono 库的核心灵魂,也是实现「计时」的基础:通过记录「开始时刻」和「结束时刻」两个时间点,计算二者的差值,就能得到这段过程的耗时。
四、核心组件 3:chrono::duration 时间间隔
4.1 什么是「时间间隔」?
chrono::duration<> 简称 duration,可以理解为:一个用来「记录两个时间点之间差值」的变量类型,表示「过去了多久」。
通俗的比方:如果 time_point 是「2026-01-19 20:00:00」,另一个 time_point 是「2026-01-19 20:00:05」,那么二者的差值就是「5 秒」,这个「5 秒」就是一个 duration 类型的时间间隔。
4.2 duration 的完整语法与模板参数解析
duration 是一个模板类,完整的语法定义是:
template<class Rep, class Period = ratio<1>>
class duration;
对新手来说不用害怕模板,我们只需要理解两个模板参数的含义,日常开发中只用到固定的几种组合,直接套用即可:
参数 1:Rep → 存储时间间隔的数值类型
- 作用:指定用什么类型的数字来存储时间间隔,比如
int(整型)、double(浮点型);
- 常用值:
int(无小数精度,比如 1 秒、200 毫秒)、double(带小数精度,比如 1.5 秒、0.35 毫秒);
- 核心建议:需要精准的小数精度用 double,只需要整数精度用 int。
参数 2:Period → 时间间隔的「单位」(核心!)
- 作用:指定这个 duration 的单位是什么,比如秒、毫秒、微秒、纳秒;
chrono 库为我们预定义了所有常用单位的别名,无需手动写复杂的 ratio 模板,直接用即可,这是开发中的必记内容:
chrono::nanoseconds → 纳秒 (1ns = 1e-9 s)
chrono::microseconds → 微秒 (1us = 1e-6 s)
chrono::milliseconds → 毫秒 (1ms = 1e-3 s)
chrono::seconds → 秒 (1s)
chrono::minutes → 分钟 (1min = 60s)
chrono::hours → 小时 (1h = 3600s)
4.3 duration 的三种常用定义方式(开发全覆盖)
方式 1:使用 chrono 预定义的单位别名(推荐,最简单,99% 场景用这个)
无需关心模板参数,直接用预定义的别名,语法简洁,可读性拉满:
#include <chrono>
using namespace std;
chrono::seconds d1(1);
chrono::milliseconds d2(200);
chrono::microseconds d3(5000);
chrono::duration<double, chrono::seconds::period> d4(1.5);
方式 2:手动指定模板参数,自定义精度和单位
适合需要特殊精度的场景,比如「以秒为单位,带小数」:
#include <chrono>
using namespace std;
chrono::duration<double> time_diff;
chrono::duration<double, chrono::seconds::period> time_diff;
重要简化规则:当 Period 参数省略时,默认就是 chrono::seconds::period(秒),所以 chrono::duration<double> 等价于 chrono::duration<double, chrono::seconds::period>,这是开发中最常用的写法!
方式 3:通过「时间点相减」自动生成(最核心,计时的标准写法)
这是 duration 最常用的生成方式,没有之一!两个 time_point 相减,会自动生成一个 duration 类型的变量,无需手动定义:
#include <chrono>
using namespace std;
auto t_start = chrono::steady_clock::now();
auto t_end = chrono::steady_clock::now();
auto time_diff = t_end - t_start;
4.4 duration 的核心成员方法:.count()(必须掌握!)
duration.count() 是 duration 类型唯一的核心成员方法,没有之一,必学必用!
核心作用
将 duration 这个「封装好的时间间隔对象」,转换成一个可以直接计算、比较、打印的「纯数字」,这个数字的单位就是我们定义 duration 时指定的单位。
语法与示例
#include <chrono>
#include <iostream>
using namespace std;
int main() {
auto t1 = chrono::steady_clock::now();
auto t2 = chrono::steady_clock::now();
chrono::duration<double> diff_sec = t2 - t1;
cout << "过去了:" << diff_sec.count() << " 秒" << endl;
chrono::milliseconds diff_ms = t2 - t1;
cout << "过去了:" << diff_ms.count() << " 毫秒" << endl;
return 0;
}
核心注意事项
duration 本身是一个对象,不是一个数字!我们不能直接用 duration 做比较、计算、打印,必须通过 .count() 方法拿到里面的纯数字才能操作。比如:
if(t2 - t1 >= 1) {}
if(chrono::duration<double>(t2 - t1).count() >= 1.0) {}
4.5 duration 的类型转换(灵活适配不同单位)
chrono 库支持不同单位的 duration 之间的隐式转换,无需手动计算,非常方便:
#include <chrono>
#include <iostream>
using namespace std;
int main() {
chrono::seconds d1(2);
chrono::milliseconds d2 = d1;
cout << d2.count() << endl;
chrono::duration<double> d3(1.5);
chrono::milliseconds d4 = chrono::duration_cast<chrono::milliseconds>(d3);
cout << d4.count() << endl;
return 0;
}
小技巧:用 chrono::duration_cast<目标类型> 可以实现强制类型转换,比如把浮点型的秒转换成整型的毫秒。
五、time_point + duration 组合使用的核心规则(必背)
这两个组件的所有用法,都围绕着两条核心语法规则展开,这是 chrono 库的基石,记住这两条,所有计时需求都能实现:
规则 1:时间点 - 时间点 = 时间间隔 (duration)
chrono::steady_clock::time_point t1, t2;
chrono::duration<double> diff = t2 - t1;
作用:计算两个时刻之间过去了多久,用于「耗时统计」「定时判断」。
规则 2:时间点 ± 时间间隔 = 新的时间点
chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
chrono::seconds d(1);
chrono::steady_clock::time_point t2 = t1 + d;
作用:计算「某个时刻之后 / 之前的多久」是哪个时刻,用于「定时任务」「延迟执行」。
六、开发中高频实战场景(全是干货,直接套用)
理论说完,最重要的是实战!结合 time_point 和 duration 的特性,整理了开发中最常用的 5 个实战场景,所有代码均可直接编译运行,涵盖 99% 的计时 / 定时需求,也是本文开头提到的实际开发需求。
前置说明
#include <iostream>
#include <chrono>
using namespace std;
场景 1:统计一段代码 / 函数的执行耗时(最常用)
需求:计算某段代码从开始到结束,一共执行了多久,精准到秒 / 毫秒 / 微秒。核心思路:记录「开始时间点」→ 执行代码 → 记录「结束时间点」→ 计算差值 → 输出耗时。
int main() {
auto t_start = chrono::steady_clock::now();
long long sum = 0;
for(long long i=0; i<1e8; ++i) {
sum += i;
}
auto t_end = chrono::steady_clock::now();
chrono::duration<double> cost_sec = t_end - t_start;
cout << "代码执行耗时:" << cost_sec.count() << " 秒" << endl;
chrono::milliseconds cost_ms = chrono::duration_cast<chrono::milliseconds>(t_end - t_start);
cout << "代码执行耗时:" << cost_ms.count() << " 毫秒" << endl;
return 0;
}
代码执行耗时:0.035621 秒
代码执行耗时:35 毫秒
场景 2:实现固定频率的任务调度(比如:1 秒执行一次)
需求:程序循环执行,但是某个任务需要严格每隔指定时间执行一次(比如 1 秒刷新一次界面文字、1 秒打印一次日志)。核心思路:记录「上次执行任务的时间点」→ 每次循环都计算「当前时间 - 上次时间」→ 差值≥指定时间就执行任务 → 更新上次时间点。
int main() {
auto last_exec_time = chrono::steady_clock::now();
while(true) {
if(chrono::duration<double>(chrono::steady_clock::now() - last_exec_time).count() >= 1.0) {
cout << "任务执行:每隔 1 秒执行一次" << endl;
last_exec_time = chrono::steady_clock::now();
}
}
return 0;
}
核心优势:这种方式是「非阻塞式定时」,不会暂停整个程序,其他业务逻辑可以正常执行,比如摄像头采集画面依然流畅,只是任务按频率执行,这是开发中的标准答案,不建议使用 Sleep()!
场景 3:实现程序的精准延迟执行(比如:延迟 500 毫秒执行)
需求:让程序在某个时刻之后,精准延迟指定时间再执行后续代码,比如延迟 500 毫秒处理数据。核心思路:计算「延迟后的目标时间点」= 当前时间点 + 延迟时间 → 循环等待直到当前时间≥目标时间点。
int main() {
cout << "开始执行,500 毫秒后继续..." << endl;
auto target_time = chrono::steady_clock::now() + chrono::milliseconds(500);
while(chrono::steady_clock::now() < target_time) {
}
cout << "延迟结束,继续执行后续代码!" << endl;
return 0;
}
场景 4:判断一段代码的执行是否超时(比如:超时 3 秒则退出)
需求:执行某个耗时操作时,如果执行时间超过指定阈值(比如 3 秒),则判定为超时并退出,避免程序卡死。核心思路:记录开始时间点 → 执行耗时操作 → 每次循环都判断是否超时 → 超时则退出。
int main() {
auto t_start = chrono::steady_clock::now();
const int timeout_sec = 3;
while(true) {
double cost = chrono::duration<double>(chrono::steady_clock::now() - t_start).count();
if(cost >= timeout_sec) {
cout << "执行超时!耗时:" << cost << " 秒" << endl;
break;
}
}
return 0;
}
场景 5:统计程序的运行总时长
需求:统计程序从启动到结束,一共运行了多久。核心思路:在程序入口记录开始时间点 → 程序结束前记录结束时间点 → 计算差值即可。
int main() {
auto t_start = chrono::steady_clock::now();
auto t_end = chrono::steady_clock::now();
chrono::duration<double> total_time = t_end - t_start;
cout << "程序运行总时长:" << total_time.count() << " 秒" << endl;
return 0;
}
七、避坑指南:新手最容易踩的 3 个坑
坑 1:用 Sleep() 代替 chrono 做定时任务
很多新手会用 Sleep(1000) 来实现 1 秒执行一次任务,这是绝对错误的!
Sleep(1000) 的作用是:让整个程序暂停 1 秒,暂停期间程序的所有业务逻辑(比如摄像头采集、界面刷新)都会卡死,无法执行;
- chrono 的定时方式是:非阻塞式定时,程序正常运行,只是任务按频率执行,其他逻辑不受影响。
结论:永远不要用 Sleep() 做定时任务,chrono 是唯一正确的选择。
坑 2:用 system_clock 做程序内计时
system_clock 和系统时间绑定,如果程序运行时手动修改了系统时间,system_clock::now() 的返回值会跳变,导致计时结果错误,甚至出现「耗时为负数」的情况。
结论:程序内计时 / 定时用 steady_clock,获取系统日历时间用 system_clock,二者各司其职。
坑 3:忘记用.count() 方法,直接操作 duration 对象
auto t1 = chrono::steady_clock::now();
auto t2 = chrono::steady_clock::now();
if(t2 - t1 >= 1) { ... }
结论:所有对 duration 的比较、计算、打印,都必须先调用 .count() 方法拿到纯数字。
八、总结(精华浓缩,快速记忆)
本文的所有知识点可以浓缩为以下几点,看完就能记住核心用法,开发中直接套用:
- 头文件:使用 chrono 库必须包含
<chrono>;
- 时钟选择:程序内计时 / 定时,推荐使用
chrono::steady_clock;
- 时间点:
chrono::steady_clock::time_point,记录具体时刻,用 steady_clock::now() 获取,核心操作是「相减」;
- 时间间隔:
chrono::duration,记录两个时刻的差值,核心方法是 .count(),用来转换成纯数字;
- 核心规则:
时间点 - 时间点 = 时间间隔,时间点 ± 时间间隔 = 新的时间点;
- 常用写法:
chrono::duration<double>(now() - last).count() >= 1.0,实现 1 秒定时;
- 避坑要点:不用 Sleep() 定时,不用 system_clock 计时,记得用 .count()。
最后
C++11 的 <chrono> 库是处理时间相关需求的方案,而 steady_clock::time_point 和 duration 是其中最核心的两个组件,二者的组合可以完美解决所有高精度计时、定时的需求。
相关免费在线工具
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
- JSON美化和格式化
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online