原文链接: Clock和Timer(chrono库)

一、Clock和Timer

  • 在过去,C和POSIX提供的系统时间接口,允许从秒转换至毫秒,再至微秒,最终至纳秒,问题是每次转换就需要一个新接口
  • 基于这个原因,C++11开始提供一个精度中立的程序库,称为chrono程序库,被定义于<chrono>中

二、Chrono程序库概述

  • Chrono程序库的设计,是希望能够处理“timer和clock在不同系统中可能不同”的事实,同时也是为了强化实践精准度
  • 为了避免像POSIX的time程序库那样每十年就引入一个新的时间类型,C++标准库的目标是提供一个精度中立概念,把duration(时间段)和timepoint(时间点)从特定clock(时钟)区分开来。最终结果就是chrono程序库核心由以下类型或概念组成:
    • duration(时间段):值得是在某时间单位上的一个明确的tick(片刻数)。例如,“3分钟”就是指一个时间段
    • timepoint(时间点):一个duration和一个epoch(起始点)的组合。例如,“2000年新年夜”就是一个时间点

  • Chrono程序库定义在<chrono>头文件中,命名空间为namespace std::chrono

三、Duration(时间段)

duration的基本格式

  • Duration是一个数值和一个分数的组合(其中的分数由ratio<>描述)

    • 模板参数1:是一个数值,用来表示时间段所经历的滴答(tick)数
    • 模板参数2:用来表示时间段每个滴答的单位(秒、毫秒等等)。可以省略,省略时默认以秒为单位
  • 例如:
  • twentySeconds:代表20秒,20*1秒=20秒(以秒为单位)
  • twentySeconds:代表0.5分钟,0.5*60秒=0.5分钟(以分钟为单位(60/1))
  • twentySeconds:代表1毫秒,1*0.001秒=1毫秒(以毫秒为单位(1/1000))
std::chrono::duration<int>                       twentySeconds(20);
std::chrono::duration<double, std::ratio<60>>    halfAMinute(0.5);
std::chrono::duration<long, std::ratio<1, 1000>> oneMillisecond(1);

chrono自带的duration类型定义

  • 在chrono中自定义了下面的类型,可以直接使用

  • 例如,下面可以轻松的指定一些时间段:
std::chrono::seconds      twentySeconds(20); //20秒
std::chrono::hours        aDay(24);          //24小时
std::chrono::milliseconds oneMillisecond(1); //1毫秒

Duration的算术运算

下图列出了duration可以进行的算术运算。例如:

  • 你可以计算两个duration的和、差、积和商
  • 你可以加减tick,或加减其他duration
  • 你可以比较两个duration的大小

运算所涉及的两个duration的单位类型可以不同:

  • 标准库的common_type<>为duration提供了一个重载版本
  • 因此运算所得的那个duration,其单位将是两个操作数的单位的最大公约数演示案例
std::chrono::seconds      d1(42); //42秒
std::chrono::milliseconds d2(10); //10毫秒d1 - d2; //返回41990个毫秒为单位的一个duration,42000-10=41990
d1 < d2; //返回false
std::chrono::duration<int, std::ratio<1, 3>> d1(1); //1/3秒
std::chrono::duration<int, std::ratio<1, 5>> d2(1); //1/5秒d1 + d2; //返回8/15秒,1/3+1/5=8/15
d1 < d2; //返回false
  • 你也可以将duration转换为不同的单位,只要彼此之间存在隐式转换即可。因此,你可以将小时转换为秒,但是反向不可以(详情还可以见下面的duration_cast<>()的介绍)。例如:
std::chrono::seconds twentySeconds(20); //20秒
std::chrono::hours   aDay(24);          //24小时std::chrono::milliseconds ms;           //0毫秒
ms += twentySeconds + aDay;             //86400000毫秒
--ms;                                   //86399999毫秒
ms *= 2;                                //172839998毫秒std::cout << ms.count() << " ms" << std::endl;
std::cout << std::chrono::nanoseconds(ms).count() << " ns" << std::endl;

Duration的其他操作

  • 下面列出了duration支持的其它操作

  • duration的默认构造函数会以默认方式初始化其数值,因此基础类型的初值是不明确的
  • duration提供三个静态函数:zero()产出一个0秒duration,min()和max()分别产出一个duration所能拥有的最小值和最大值
  • 例如,下面为duration对象添加一个operator<<版本
template<typename V,typename R>
std::ostream& operator<<(std::ostream& s, const std::chrono::duration<V, R>& d)
{s << "[" << d.count() << " of " << R::num << "/" << R::den << "]";return s;
}int main()
{std::chrono::milliseconds d(42);std::cout << d << std::endl;
}

duration_cast<>

  • 在上面我们介绍过duration的类型转换,可以将一个低精度的单位类型转换为一个高精度的单位类型(例如,将分钟转换为秒,将秒转换为微秒),但是不能将一个高精度的单位类型转换为一个低精度的单位类型(例如,将微秒转换为秒,将秒转换为分钟等。因为这可能会造成数据的丢失,例如将42010毫秒转换为秒,结果是42,那么原本的10毫秒就丢失了)
  • 如果想要将高精度的单位类型转换为一个低精度的单位类型,那么可以使用duration_cast<>进行强制转换
  • 例如:
std::chrono::seconds sec(55);//错误的,默认不能将秒转换为分钟
std::chrono::minutes m1 = sec;//正确的,可以使用duration_cast,将秒转换为分钟
std::chrono::minutes m2 = std::chrono::duration_cast<std::chrono::minutes>(sec);
  • 将浮点数类型的duration转换为整数类型的duration也需要使用duration_cast<>。例如:
std::chrono::duration<double, std::ratio<60>> halfMin(0.5);//错误,halfMin的tick为double类型,s1的tick默认为int类型
std::chrono::seconds s1 = halfMin;//正确,使用duration_cast强制转换
std::chrono::seconds s2 = std::chrono::duration_cast<std::chrono::seconds>(halfMin)
  • 演示案例:

    • 下面代码把duration切割为不同单元,例如让一个以毫秒为单位的duration切割为相对应的小时、分钟、秒钟、毫秒
    • 在下面我们将ms转换为小时hh,实际数值会被截断而四舍五入
    • 幸好有%运算符,我们可以把一个duration当做其第二实参,于是写下ms%std::chrono::hours(1)轻松处理剩余的毫秒,那么毫秒又被转换为分钟
std::chrono::milliseconds ms(7255042);std::chrono::hours hh = std::chrono::duration_cast<std::chrono::hours>(ms);
std::chrono::minutes mm = std::chrono::duration_cast<std::chrono::minutes>(ms%std::chrono::hours(1));
std::chrono::seconds ss = std::chrono::duration_cast<std::chrono::seconds>(ms%std::chrono::minutes(1));
std::chrono::milliseconds msec = std::chrono::duration_cast<std::chrono::milliseconds>(ms%std::chrono::seconds(1));std::cout << "raw: " << hh << "::" << mm << "::" << ss << "::" << msec << std::endl;
std::cout << "     " << setfill('0') << setw(2) << hh.count() << "::"<< setw(2) << mm.count() << "::"<< setw(2) << ss.count() << "::"<< setw(3) << msec.count() << std::endl;

四、Clock(时钟)

  • Clock(时钟):

    • 定义一个epoch(起始点)和一个tick周期
    • 例如,某个clock也许定义tick周期为毫秒,起始点是UNIX epoch
    • 此外,clock还提供一个类型给“与此clock关联”的任何timepoint使用
    • Clock提供的函数now()可以产出一个代表“现在时刻”的timepoint对象

Clock提供的操作

  • 下图列出了clock提供的类型定义和static成员

三个clock

  • 标准库提供了三个clock:

    • system_clock:它所表现的timepoint将关联至现行系统的即时时钟

      • 这个clock提供便捷函数to_time_t()和from_time_t(),允许我们在timepoint和“C的系统时间类型”timet之间转换,这意味着你可以转换至日历时间
    • strady_clock:它保证绝不会被调用,因此当实际时间流逝,其timepoint值绝不会减少,而且这些timepoint相对于真实时间都有稳定的前进速率
    • high_resolution_clock:它所表现的是当前系统中带有最短tick周期的clock
  • 这三个clock都支持上面Clock提供的操作

三个clock的精度问题

  • 标准库并不强制规定上述clock的精准度、epoch,“最小和最大timepoint的范围”。举个例子,你的system clock也许提供的是UNIX epoch(1970年1月1日),如果你还需要一个特定的epoch,或你关注的timepoint并非被你的clock涵盖,你就必须使用各种便捷函数查清楚
  • 例如,下面的函数打印某个clock的各种属性:
template<typename C>
void printClockData()
{std::cout << "- precision: ";typedef typename C::period P;if (std::ratio_less_equal<P, milli>::value){typedef typename std::ratio_multiply<P, kilo>::type TT;std::cout << fixed << double(TT::num) / TT::den << " milliseconds" << std::endl;}else {std::cout << fixed << double(P::num) / P::den << " seconds" << std::endl;}std::cout << "- si_steady: " << boolalpha << C::is_steady << std::endl;
}
  • 我们可以针对各种clock调用这个函数,例如:
int main()
{std::cout << "system_clock: " << std::endl;printClockData<std::chrono::system_clock>();std::cout << "\nhigh_resolution_clock: " << std::endl;printClockData<std::chrono::high_resolution_clock>();std::cout << "\nsteady_clock: " << std::endl;printClockData<std::chrono::steady_clock>();
}
  • 结果如下图所示:

    • 可以看到,system_clock和high_resolution_clock有着相同精度,100纳秒,而steady_clock的精度则是毫秒
    • 还可以看到,steady_clock和high_resolution_clock不能被调整
    • 还需要注意,在其他系统中情况也许完全不同,例如high_resolution_clock有可能和system_clock相同

演示案例

  • 用来比较程序的两个时间点是否相同,或计算差距,system_clock扮演者重要角色。例如:
//获得当前时间,保存为system_start
auto system_start = std::chrono::system_clock::now();//...//再次获取当前时间,与system_start进行比较,查看时间是否超过了1分钟
if (std::chrono::system_clock::now() > system_start + std::chrono::minutes(1))
{//...
}
  • 上面的代码可能行不通,因为如果clock在中间被调整,即使程序执行超过了1分钟,if也可能返回false
  • 类似的,当我们处理程序的执行时间,下面的程序有可能因中间clock被调用而打印出一个负值duration
#include <windows.h> //Sleepint main()
{//获得当前时间,保存为system_startauto system_start = std::chrono::system_clock::now();Sleep(3000); //休息3秒//获得时间差auto diff = std::chrono::system_clock::now() - system_start;//强制转换为秒auto sec = std::chrono::duration_cast<std::chrono::seconds>(diff);//打印std::cout << "this program runs:" << sec.count() << " seconds" << std::endl;
}

  • 基于相同的理由,使用timer搭配steady_clock以外的lock,有可能一旦system_clock被调用它们的duration也随之改变

五、Timepoint(时间点)

  • Timepoint(时间点)

    • 表现出某个特定时间点,关联至某个clock的某个正值或负值duration
    • 例如,如果duration是10天,其关联的clock epoch是“1970年1月1日”,那么这个timepoint就是1970年1月11日
    • timepoint提供的能力包括:产出epoch、产出“与其clock相应”的所有timepoint中的最小值和最大值,以及timepoint的各种算术运算
  • timepoint定义如下:

  • timepoint分为下面四种类型:

    • Epoch:由任何clock的time_point的默认构造函数产出
    • Current time:由任何clock的static成员函数now()产出
    • Minimum timepoint:由任何clock的time_point的static成员函数min()产出
    • Maximum timepoint:由任何clock的time_point的static成员函数max()产出

演示案例1

  • 下面程序将timepoint赋值给tp并转换为日历表示法,然后打印出来:
std::string asString(const std::chrono::system_clock::time_point& tp)
{std::time_t t = std::chrono::system_clock::to_time_t(tp);std::string ts = std::ctime(&t);ts.resize(ts.size() - 1);return ts;
}int main()
{std::chrono::system_clock::time_point tp;std::cout << "epoch: " << asString(tp) << std::endl;tp = std::chrono::system_clock::now();std::cout << "now: " << asString(tp) << std::endl;tp = std::chrono::system_clock::time_point::min();std::cout << "min: " << asString(tp) << std::endl;tp = std::chrono::system_clock::time_point::max();std::cout << "max: " << asString(tp) << std::endl;
}

timepoint提供的操作

  • timepoint提供的操作如下:

  • time_since_epoch():

    • 一般而言,timepoint对象只有一个成员,是个duration,与相应的clock的epoch有着关系
    • 这个timepoint值可以通过time_since_epoch()取得

timepoint的溢出

  • 虽然timepoint的接口用了ratio,这确保了duration单元的溢出会导致编译器报错,但是duration的溢出还是可能会发生
  • 见下面的例子:

  • 这也说明了chrono是一个duration/timepoint程序库,而不是个date/time程序库。你可以计算duration和timepoint但仍然必须把epoch、最小和最大的timepoint、闰年和闰秒纳入考虑

六、C和POSIX提供的Date/Time函数

  • C和POSIX提供的Date/Time函数介绍参阅:https://blog.csdn.net/qq_41453285/article/details/102651298
  • C++标准也提供了C和POSIX所提供的“处理date和time”接口。原本在<time.h>内的宏、类型、函数,现在被定义在<ctime>的namespace std内
  • <ctime>所提供的操作如下图所示:
    • 宏CLOCK_PER_SEC定义了clock()的单位类型(它返回的是elapsed CPU time,以1/CLOCK_PER_SEC秒计)
    • time_t通常只是“始自UNIX epoch(1970年1月1日)的秒数”。然而根据C和C++标准的说法,也并不保证如此

演示案例(timepoint和日历时间的转换)

七、以计时器停滞线程

  • Duration和timepoint可用于线程或程序(即主线程)的停滞(block)。停滞可以是无条件的,也可以指定最大时间段,或等待一个lock或某条件成立,或等待另一线程结束
  • 提供的操作如下:
    • sleep_for()和sleep_until():由this_thead提供用以停滞线程
    • try_lock_for()和try_lock_until():用来在等待一个mutex时指定最大时间段
    • wait_for()和wait_until():用来在等待某条件成立或等待一个future时指定最大时间段
  • 所有以...for()结尾的函数都会用到一个duration,所有以...until()结束的函数都会用到一个timepoint。例如:
    • this_thread::sleep_for(chrono::seconds(10));   //会停滞当前线程(不无可能是主线程)10秒钟
    • this_thread::sleep_until(chrono::system_clock::now()+chrono::seconds(10));  //停滞当前线程,直到system clock 来到一个“比此刻多10秒”的timepoint
  • 这些调用虽然看起来相同,其实不然:
    • 所有...until()函数,你需要传递一个timepoint,而它收到时间调整的影响。如果sleep_until()之后的10秒内system clock被调整了,停滞时间也会被相应调整。例如我们把system clock回调1小时,这个程序将被停滞60分钟又10秒。又如果我们把clock调快超过10秒,timer会立刻结束
    • 如果使用...for()函数如sleep_for(),你必须传一个duration,或你使用steady_clock,那么system clock的调整通常不会影响duration。然而在一个不提供steady clock的硬件上,软件平台没机会在不受到“可能被调整的system time”的影响下计算秒数,因此时间的调整也会冲击...for()函数
  • 所有这些timer都不会保证绝对精准。对任何timer而言都存在一点点延迟,因为系统只是周期性地检查那个timer结束了,而timer和interrupt(中断)的处理又需要花费一些时间。因此,timer的时间长度将会是它们所指定的时间加上一小段(取决于实现的质量和当下情势)

C++ Clock和Timer(chrono库)相关推荐

  1. C++(标准库):10---Clock和Timer(chrono库)

    一.Clock和Timer 在过去,C和POSIX提供的系统时间接口,允许从秒转换至毫秒,再至微秒,最终至纳秒,问题是每次转换就需要一个新接口 基于这个原因,C++11开始提供一个精度中立的程序库,称 ...

  2. C++11 处理时间和日期的处理,以及chrono库介绍

    C++11提供了日期和时间相关的库chrono,通过这个库可以很方便的对时间和日期进行处理: chrono库主要包含三种类型: 1.时间间隔duration 介绍一下duraton的原型: templ ...

  3. C++11 中chrono库 实现高精度定时

    一种"传统"ctime计时方法: #include <ctime> using namespace std;clock_t start = clock(); // do ...

  4. C++11的chrono库,可实现毫秒微秒级定时

    C++11有了chrono库,可以很容易的实现定时功能. chrono: chrono库主要包含了三种类型:时间间隔Duration.时钟Clocks和时间点Time point. Duration: ...

  5. chrono是一个time library,C++11 std::chrono库详解

    C++11 std::chrono库详解 chrono是一个time library, 源于boost,现在已经是C++标准.话说今年似乎又要出新标准了,好期待啊! 要使用chrono库,需要#inc ...

  6. (笔记)处理日期和时间的方法(chrono 库,time_t等)

    文章目录 0 摘要 1 chrono 库 1.1 时间间隔duration 1.2 时间点time point 1.3 时钟clocks 1.3.1 system_clock 1.3.2 steady ...

  7. C++处理日期和时间的chrono库

    C++11 中提供了日期和时间相关的库 chrono,通过 chrono 库可以很方便地处理日期和时间,为程序的开发提供了便利.chrono 库主要包含三种类型的类:时间间隔duration.时钟cl ...

  8. 时间编程Chrono库 - C++11

    chrono库主要包含了三种类型:时间间隔Duration.时钟Clocks和时间点Time point.时钟是系统内部不断循环执行的时钟,某个一点对应的是时间点timepoint,两个时间点之间的差 ...

  9. C++11 chrono 库

    C++11 chrono 图解 1 设计一个时间日期库需要哪些要素 1.1 时间的计量形式 1.2 时间的计量精度 1.3 时间点 2 chrono库类 2.1 三种时钟 2.2 多种精度 2.3 时 ...

最新文章

  1. Spring Cloud剖析--云平台技术栈17
  2. R计算数组的累计加和(Cumulative Sums)
  3. frame,iframe,frameset 的区别
  4. Bash数组变量的赋值
  5. C# winFrom窗体设计问题-部分文件打不开窗体设计器 变成类.cs
  6. ACdream 1417 Numbers
  7. html5 ios 音乐加载失败,Cocos2d-JS HTML5的cc.audioEngine音乐音效在iOS设备上不响应静音键的问题...
  8. 【CSS】浅谈 CSS 中常用的相对长度单位 em, rem
  9. 【C语言】打印二叉树树形(制表符实现,清晰+高拓展)(2022-10-22 更新—偏移量说明)
  10. Zoomit的简单使用
  11. GTD时间管理简洁做法
  12. laravel查询按照whereIn排序
  13. HCNP——RIPv1和RIPv2概况
  14. MySQL攻略(2)
  15. 龙族幻想最新东京机器人位置_龙族幻想藤原智坐标位置一览 藤原智任务攻略...
  16. Electron MAC 打包签名生成
  17. 鸿蒙手机卸载内置应用 adb连接
  18. 数据挖掘与分析——回归模型
  19. JavaScript基本原理常识
  20. Word中批量更新域的两个小方法

热门文章

  1. VR全景的商业潜力从哪里体现?市场前景怎么样?
  2. 采用Java方式实现数据同步
  3. gdtool解决在微信中点击棋牌游戏类app下载链接显示已停止访问打不开的问题
  4. wwwroot中的文件删不掉
  5. mapboxGL中popup遮挡的优化
  6. 你的孩子为什么要学Scratch?
  7. 如何将年薪从十五万到五十万系列之从一个sql引发的hive谓词下推的全面复盘及源码分析(上)
  8. 【论文学习】行人检测——CVPR:通过MIMS在低分辨率图像中做行人检测
  9. js 模板字符串 嵌套
  10. WM8988 配置和注意事项