news 2026/7/4 20:20:21

C++ STL 之 chrono 时间库详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ STL 之 chrono 时间库详解

C++ STL 之 chrono 时间库详解

为什么需要 chrono

C 标准库的<ctime>提供time_tclock_tstruct tmclock()/time()等接口,但存在三个硬伤:

  • 精度不可控time_t只能到秒,clock()虽返回 ticks 但单位由CLOCKS_PER_SEC定义,不同平台不一致
  • 类型不安全:秒和毫秒都是算术类型,编译器不会阻止你把time_t直接赋值给毫秒变量
  • 单调性无保证time()返回墙上时间,系统时间被 NTP 同步或用户手动修改后可能倒退,测性能时会出现负数间隔

<chrono>(C++11 引入,C++20/23 大幅扩展)用强类型系统和编译期有理数运算解决了上述所有问题。


三种时钟

时钟是 chrono 的基石。C++ 标准定义了三种时钟,各有用途。

chrono 时钟体系

system_clock
墙上时间(挂钟时间)

steady_clock
单调递增时间

high_resolution_clock
最高精度时钟

特点:可转 time_t
会被 NTP 调整

用途:日志时间戳
文件修改时间

特点:严格单调
永不回调

用途:性能测量
函数耗时

特点:通常是对
steady_clock 的类型别名

用途:微基准测试

system_clock

表示系统范围的实时挂钟(wall clock),对应time_t。可调用to_time_t()转成传统time_t,也支持from_time_t()反向转换。

#include<chrono>#include<iostream>#include<iomanip>intmain(){autotp=std::chrono::system_clock::now();std::time_t t=std::chrono::system_clock::to_time_t(tp);std::cout<<"当前时间: "<<std::put_time(std::gmtime(&t),"%F %T")<<'\n';}

steady_clock

单调时钟,保证了now()的返回值永远不会减小。适合测量代码段耗时:

#include<chrono>#include<iostream>#include<thread>intmain(){autostart=std::chrono::steady_clock::now();std::this_thread::sleep_for(std::chrono::milliseconds(100));autoend=std::chrono::steady_clock::now();autodiff=end-start;automs=std::chrono::duration_cast<std::chrono::milliseconds>(diff);std::cout<<"耗时: "<<ms.count()<<" ms\n";}

high_resolution_clock

字面意思是"最高精度时钟"。在主流实现中(MSVC、libstdc++、libc++),它通常是steady_clock的类型别名,而非独立时钟。不要假定它与steady_clock不同。


duration:编译期单位转换

duration是 chrono 最精巧的设计:把数值和单位打包成一个类型

模板签名:

template<classRep,classPeriod=ratio<1>>classduration;
  • Rep:计数值类型(int64_tdouble等)
  • Period:编译期有理数,表示"每个 tick 对应多少秒"。ratio<1, 1000>表示毫秒,ratio<1, 1'000'000>表示微秒

标准库预定义了常用单位:

类型定义
nanosecondsduration<int64_t, nano>
microsecondsduration<int64_t, micro>
millisecondsduration<int64_t, milli>
secondsduration<int64_t>
minutesduration<int64_t, ratio<60>>
hoursduration<int64_t, ratio<3600>>

单位转换链如下:

hours
ratio<3600>

minutes
ratio<60>

seconds
ratio<1>

milliseconds
ratio<1,1000>

microseconds
ratio<1,1'000'000>

nanoseconds
ratio<1,1'000'000'000>

duration_cast / floor
可编译期转换(降精度需要显式)

隐式转换 vs 显式转换

从低精度到高精度(无损)可以隐式转换:

automs=std::chrono::milliseconds(1500);std::chrono::microseconds us=ms;// OK,1500'000 us

从高精度到低精度(可能截断)必须用duration_cast

autous=std::chrono::microseconds(2500);automs=std::chrono::duration_cast<std::chrono::milliseconds>(us);// 2 ms(截断)

C++17 新增floorceilround

usingnamespacestd::chrono;automs=duration_cast<milliseconds>(microseconds(2500));// 2automs2=floor<milliseconds>(microseconds(2500));// 2automs3=ceil<milliseconds>(microseconds(2500));// 3automs4=round<milliseconds>(microseconds(2499));// 2

time_point:时间轴上的一个点

time_point绑定到特定时钟,表示从该时钟的 epoch 起经过的 duration。

template<classClock,classDuration=typenameClock::duration>classtime_point;

关键成员:

  • time_since_epoch():返回 epoch 到该点之间的duration
  • operator +/operator -:与duration做算术
autotp=std::chrono::system_clock::now();autod=tp.time_since_epoch();automs=std::chrono::duration_cast<std::chrono::milliseconds>(d);std::cout<<"Unix 时间戳(毫秒): "<<ms.count()<<'\n';

不同时钟的time_point不能混用。system_clock::time_pointsteady_clock::time_point是不同的类型,编译器禁止直接运算。


C++20 日历扩展

C++20 为 chrono 增加了日历类型,彻底终结了tm+mktime的繁琐模式。

year_month_day

#include<chrono>intmain(){usingnamespacestd::chrono;autotoday=floor<days>(system_clock::now());year_month_day ymd{today};// 构造指定日期year_month_day ymd2{2026y,June,25d};autod=sys_days{ymd2};autotp=system_clock::time_point{d};}

工作日计算

#include<chrono>#include<iostream>intmain(){usingnamespacestd::chrono;// 2026-06-25 是星期几?year_month_day ymd{2026y,June,25d};autowd=weekday{sys_days{ymd}};std::cout<<"weekday: "<<wd<<'\n';// 下一个周一autodays_until_mon=(Monday-wd).count();if(days_until_mon<=0)days_until_mon+=7;autonext_mon=sys_days{ymd}+days{days_until_mon};}

日期加减

autod=sys_days{2026y,January,1d}+months{3}-days{5};// 约 2026-03-27

注意事项

  • year_month_day存储在days精度,通过sys_days{}system_clock::time_point互转
  • C++20 日期支持在 GCC 12+、Clang 16+、MSVC 2022 17.0+ 中可用
  • 需要链接-latomic(某些平台)

性能:high_resolution_clock::now() 开销

now()的调用成本因平台/时钟而异:

时钟近似耗时说明
system_clock::now()~25-50 ns通常调用GetSystemTimePreciseAsFileTime(Win)或clock_gettime(CLOCK_REALTIME)(Linux)
steady_clock::now()~25-50 ns与 system_clock 接近,同样基于QueryPerformanceCounterCLOCK_MONOTONIC
high_resolution_clock::now()~同样本质上是上述之一的别名

结论:单次now()开销约 50 ns,在宏观性能测量中可忽略;但在微基准测试中(测量耗时 < 1 us 的操作),需要重复执行并取平均值以稀释 overhead。

// 测量高精度小函数的正确姿势autoinvoke=[&](){/* 被测函数 */};autostart=std::chrono::steady_clock::now();for(inti=0;i<100'000;++i)invoke();autoend=std::chrono::steady_clock::now();autoavg=(end-start)/100'000;std::cout<<"平均耗时: "<<std::chrono::duration_cast<std::chrono::nanoseconds>(avg).count()<<" ns\n";

面试题

1.steady_clocksystem_clock的根本区别是什么?分别用于什么场景?

steady_clock保证单调递增,不受系统时间调整影响,用于性能测量。system_clock表示墙上时间,可转time_t,用于时间戳和日志记录。

2. 为什么不能用steady_clock打印"当前时间"?

steady_clock的 epoch 通常是系统启动时间(而非 Unix 纪元),无法映射到人类可读的日历时间。要打印当前时间必须用system_clock

3.duration_castfloor/ceil/round有什么区别?

duration_cast向零截断(truncate toward zero),floor向下取整、ceil向上取整、round四舍五入到最近的整数。对正数duration_castfloor结果相同,但对负数不同。

4. 为什么high_resolution_clock::now()不能保证高精度?

它是实现定义的别名,在主流实现中就是steady_clocksystem_clock之一,并未提供额外精度。steady_clock::now()实际精度受操作系统定时器分辨率限制(Windows 默认 ~15.6 ms,除非使用多媒体定时器)。

5. 如何将system_clock::time_point转为 C++20 的year_month_day

autotp=std::chrono::system_clock::now();autodays=std::chrono::floor<std::chrono::days>(tp);std::chrono::year_month_day ymd{days};

6.durationPeriod参数是运行时指定的还是编译期指定的?它如何影响代码生成?

编译期指定。Periodstd::ratio的特化,所有单位转换通过有理数算术在编译期完成。编译器能在常数时间内完成乘除优化,运行时零开销。

7. C++20 之前,如何计算两个日期之间的天数差?C++20 有什么改进?

C++20 之前需要用mktimetime_t再相除86400,但受时区影响可能有偏差。C++20 的sys_days将日期映射到 days 精度的时间点,直接相减就是精确天数差,且不涉及时区。

autod1=sys_days{2026y,January,1d};autod2=sys_days{2026y,December,31d};autodiff=(d2-d1).count();// 364

8. 什么是 epoch?system_clocksteady_clock的 epoch 分别是什么?

epoch 是时间轴的零点。system_clock的 epoch 是 1970-01-01 00:00:00 UTC(Unix 纪元)。steady_clock的 epoch 是平台相关的(通常是系统启动时间),且不保证不同进程间一致。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/4 20:20:18

MNIST手写数字识别:CNN实现与优化实战

1. MNIST数字识别项目概述MNIST手写数字识别是计算机视觉领域的"Hello World"项目&#xff0c;这个经典数据集包含0-9共10个类别的6万张训练图片和1万张测试图片。每张都是2828像素的灰度图像&#xff0c;数据经过标准化处理&#xff0c;非常适合作为深度学习入门练手…

作者头像 李华
网站建设 2026/7/4 20:14:58

3步实现单机多人分屏游戏:Nucleus Co-Op分屏工具完全指南

3步实现单机多人分屏游戏&#xff1a;Nucleus Co-Op分屏工具完全指南 【免费下载链接】nucleuscoop Starts multiple instances of a game for split-screen multiplayer gaming! 项目地址: https://gitcode.com/gh_mirrors/nu/nucleuscoop 你是否曾梦想过与朋友们一起坐…

作者头像 李华
网站建设 2026/7/4 20:12:31

TVA在具身智能商业化部署中的技术突破(13)

前沿技术介绍&#xff1a;AI智能体视觉&#xff08;TVA&#xff0c;Transformer-based Vision Agent&#xff09;是依托Transformer架构与“因式智能体”理论所构建的颠覆性工业视觉技术&#xff0c;属于“物理AI” 领域的一种全新技术形态&#xff0c;完成了从“虚拟世界”到“…

作者头像 李华
网站建设 2026/7/4 20:00:33

数据产业服务分类(21)——数据要素——概述

本章节在明确生产要素之间关系的基础上&#xff0c;重点探讨数据要素与其他各个生产要素之间的转化关系。研究数据要素与其他生产要素的关系&#xff0c;在数据产业服务分类方案研究中为构建科学、合理且贴合产业实际的服务分类体系指引方向&#xff0c;发挥着多维度的关键作用…

作者头像 李华
网站建设 2026/7/4 19:57:03

【MySQL】一文读懂 MySQL 事务控制与 MVCC 多版本并发控制底层原理

&#x1f525;个人主页&#xff1a;Cx330&#x1f338; ❄️个人专栏&#xff1a;《C语言》《LeetCode刷题集》《数据结构-初阶》《C知识分享》 《优选算法指南-必刷经典100题》《Linux操作系统》:从入门到入魔 《Git深度解析》:版本管理实战全解 《Qt 极境架构》MySQL 核心…

作者头像 李华
网站建设 2026/7/4 19:56:37

免费分享最新IDEA安装及授权教程(附带文件)

前言 大家好&#xff0c;我是Ktiiy学姐&#x1f44b;。刚入驻 CSDN&#xff0c;以后会持续更新&#xff0c;给大家免费零基础开发环境搭建、项目源码、避坑教程、面试技巧等&#xff01;点关注不迷路 今天给大家带来IDEA 完整纯净安装配置永久授权教程&#xff0c;全程无废话…

作者头像 李华