本文是我根据《The C++ Standard Library Second Edition》自己总结。本书成书于C++11时期
目的
Chrono Library 是一个精度中立的时间相关库。解决两个问题:
- duration 持续时间
- time point 时间点
可以和basic C和 POSIX接口掌管的日历时间,以及C++11加入的thread多线程并发库组成很多新的玩法。
基本概念
duration 由数字和单位组成来表示。单位就是时分秒微秒。数字就是“No. of ticks”。
time point 由duration+epoch(起始时间)表示。A typical example is a timepoint that represents “New Year’s Midnight 2000,” which is described as “1,262,300,400 seconds since January 1, 1970” (this day is the epoch of the system clock of UNIX and POSIX systems). 这个起始时间在程序里很常用?不占存储空间吗?
Note that all identifiers of the chrono library are defined in namespace std::chrono
.
Durations
背后基本的单位是秒,模板使用std:ratio
通过分数的形式实现别的单位。
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);
这种方式可以将1秒任意分割,成不常用的尺度。比如1/3秒。
官方定义单位:
namespace std {
namespace chrono {
typedef duration<signed int-type >= 64 bits,nano> nanoseconds;
typedef duration<signed int-type >= 55 bits,micro> microseconds;
typedef duration<signed int-type >= 45 bits,milli> milliseconds;
typedef duration<signed int-type >= 35 bits> seconds;
typedef duration<signed int-type >= 29 bits,ratio<60>> minutes;
typedef duration<signed int-type >= 23 bits,ratio<3600>> hours;
} }
这样在使用中可以直接使用单位:
std::chrono::seconds twentySeconds(20);
std::chrono::hours aDay(24);
std::chrono::milliseconds oneMillisecond(1);
Durations对算数运算符和逻辑运算符都有重载。并且单位自动变成能正确容纳结果的单位。
简单举例:
chrono::seconds d1(42); // 42 seconds
chrono::milliseconds d2(10); // 10 milliseconds
d1 - d2 结果是41,990 ticks of unit type milliseconds .
隐式类型转换:
std::chrono::seconds twentySeconds(20); // 20 seconds
std::chrono::hours aDay(24); // 24 hours
std::chrono::milliseconds ms; // 0 milliseconds
ms += twentySeconds + aDay; // 86,400,000 milliseconds
--ms; // 86,399,999 milliseconds
ms *= 2; // 172,839,998 milliseconds
std::cout << ms.count() << " ms" << std::endl;
std::cout << std::chrono::nanoseconds(ms).count() << " ns" << std::endl;
These conversions result in the following output:
172839998 ms
172839998000000 ns
构造函数
duration d
duration d(d2)
duration d(val)
d = d2
其他的方法
d.count()
返回当前ticks数
duration_cast<D>(d)
Returns duration d explicitly converted into type D
duration::zero() Yields duration of zero length
duration::max() Yields maximum possible duration of this type
duration::min() Yields minimum possible duration of this type
duration::rep Yields the type of the ticks
duration::period Yields the type of the unit type
示例代码:
细粒度单位向粗粒度单位必须使用cast强制转换、截断数据。
如果是小数tick向整数单位转换也必须使用duration_cast.
using namespace std;
using namespace std::chrono;
milliseconds ms(7255042);
// split into hours, minutes, seconds, and milliseconds
hours hh = duration_cast<hours>(ms);
minutes mm = duration_cast<minutes>(ms % chrono::hours(1));
seconds ss = duration_cast<seconds>(ms % chrono::minutes(1));
milliseconds msec
= duration_cast<milliseconds>(ms % chrono::seconds(1));
// and print durations and values:
cout << "raw: " << hh << "::" << mm << "::"
<< ss << "::" << msec << endl;
cout << " " << setfill(’0’) << setw(2) << hh.count() << "::"
<< setw(2) << mm.count() << "::"
<< setw(2) << ss.count() << "::"
<< setw(3) << msec.count() << endl;
Clock(时钟)和Timepoint(时间点)
时钟和时间点相互依存。
clock调用now()产出一个时间点。
C++11提供三种clock:
- steady_clock,稳定时钟,保证每时每刻以稳定速率增长 这个一般可能精度较低。
- system_clock,系统时钟,可能不稳定,因为人或其它程序可以修改。
- high_resolution_clock,高精度钟,拥有最小系统可提供的时间单位 。根据不同的硬件,可能是steady。
要统计程序运行时间,比较时间点这种情况,必须使用steady的时钟。
clock::is_steady
:当前时钟是否是steady
clock::now()
:产出当前时间点 time point
Timepoint支持算数逻辑运算符重载,
另外的方法:
tp.time_since_epoch() 返回从Epoch开始的duration,epoch由default构造函数构造。
返回从epoch时间到此刻的时间段。epoch时间是时钟的开启时间。对于system_clock和high_resolution_clock来说,epoch是1970-01-01T00:00:00。steady_clock时钟则是boot启动时间,所以不应该对steady_clock::now()返回的time_point调用time_since_epoch()。
那么我的想法是:先看一下高精时钟是否steady,如果是的话,使用高精时钟进行代码运行时间统计。
通过计时器控制线程执行
sleep_for() 和 sleep_until(),由 this_thread 提供用以停滞线程
try_lock_for() 和 try_lock_until(),用来在等待一个 mutex 时指定最大时间段
wait_for()和wait_until(),用来在等待某条件成立或等待一个future时指定最大时间段
所有以…_for()结束的停滞函数都会用到一个duration,所有以…_until()结束的函数都会用到一个timepoint。
this_thread::sleep_for(chrono::seconds(10));
this_thread::sleep_until(chrono::system_clock::now()+ chrono::seconds(10));
这些调用虽然看起来相同,其实不然。面对所有…_until()函数,你需要传递一个timepoint,而它会受到时间调整的影响。如果调用 sleep_until()之后的10秒内system clock被调整了,停滞时间也会被相应调整。例如我们把system clock回调1小时,这个程序将被停滞60分钟又10秒。又如果我们把clock调快超过10秒,timer会立刻结束。
如果使用…_for()函数如sleep_for(),你必须传一个duration,抑或你使用steady_clock,那么system clock的调整通常不会影响duration。
所有这些timer都不保证绝对精准。对任何timer而言都存在一点点延迟,因为系统只是周期性地检查哪个timer结束了,而timer和interrupt(中断)的处理又需要花费一些时间。因此,timer的时间长度将会是它们所指定的时间加上一小段(取决于实现的质量和当下情势)。