C++11 chrono解析

C++11 chrono解析

chrono 是C++11的时间库,提供了计时、时钟等功能。

学习chrono, 关键是理解里面时间段(Durations)、时间点(Time points)的概念。

精度

时钟节拍(时间精度):

template<intmax_t N, intmax_t D = l> class ratio;

其中:N表示分子,D表示分母,默认用秒表示的时间单位。

N对应于其成员num,D对应于其成员den

常用的单位:

1
2
3
4
ratio<60, 1> minute
ratio<1, 1> second
ratio<1, 1000> microsecond
...

ratio主要是为后面将要讲解的时间段,时间点等提供精度(单位)

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <chrono>

using namespace std;

int main()
{
cout << "millisecond:";
cout << std::chrono::milliseconds::period::num << "/" << std::chrono::milliseconds::period::den << "s" << endl;

return 0;
}

时间段:

1
2
template<class Rep, class Period = ratio<l> >
class duration;

std::chrono::duration 表示一段时间,比如两个小时,12.88秒,半个时辰,一炷香的时间等等。

Rep表示一种数值类型,用来表示Period的数量,比如int float double。

Period是ratio类型,用来表示上面所说的单位精度,比如second millisecond。

chrono中宏定义了许多特例了的duration:

就是常见的hours,minutes,seconds,milliseconds等,使用std::chrono::milliseconds直接使用。

(1)构造函数很简单

1
2
3
4
5
6
(1) duration() = default;	// 默认构造
(2) duration(const duration& dtn); // (2)(3) 拷贝构造
(3) template<class Rep2, class Periods>
constexpr duration(const duration<Rep2, Periods2>& dtn);
(4) template<class Rep2> // 传递一个某类型(int等)的数值,构造一个时间段
constexpr explicit duration(const Rep2& n);

(2) 成员函数count() 返回单位时间的数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <chrono>

int main()
{
std::chrono::milliseconds msecond(1000); // 1 second
std::cout << msecond.count() << " milliseconds.\n";

std::cout << msecond.count() * std::chrono::milliseconds::period::num / std::chrono::milliseconds::period::den;

std::cout << " seconds.\n";
return 0;
}

(3) 当不要截断值的情况下(时转换成秒是没问题,但是秒转换成时就不行)时间段的转换是隐式的。显示转换可以由 std::chrono::duration_cast<>来完成。

比如 std::chrono::milliseconds ms(54802);

std::chrono::seconds s = std::chrono::duration_caststd::chrono::seconds(ms);

这里的结果就是截断的,而不是进行了舍入,所以s最后的值将为54。

时间点:

1
2
template<class Clock, class Duration = typename Clock::duration>
class time_point;

std::chrono::time_point 表示一个具体时间,如上个世纪80年代、今天下午3点、火车出发时间等,只要它能用计算机时钟表示。

第一个模板参数Clock用来指定所要使用的时钟(标准库中有三种时钟,system_clock,steady_clock和high_resolution_clock。见 时钟详解 ),第二个模板函数参数用来表示时间的计量单位(特化的std::chrono::duration<>

时间点都有一个时间戳,即时间原点。chrono库中采用的是Unix的时间戳1970年1月1日 00:00。所以time_point也就是距离时间戳(epoch)的时间长度(duration)。

(1)构造函数

1
2
3
4
5
6
time_point(); // 默认构造函数,时间戳作为其值

template<class Duration2>
time_point(const time_point<clock, Duration2>& tp); // 拷贝构造函数

explict time_point(const duration& dtn); // 使用duration构造,就是距离时间戳的时间长度

(2) 时间点有个重要的函数:duration time_since_epoch() (用于获取当前时间点距离时间戳的时间长度)

即经常用来得到当前时间点到1970年1月1日00:00的时间距离、该函数返回的duration的精度和构造time_point的时钟(Clock)有关(见 时钟详解)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <chrono>
#include <ctime>

using namespace std;

int main()
{
// 距离时间戳2秒
chrono::time_point<chrono::system_clock, chrono::seconds> tp(chrono::seconds(2));

cout << "to epoch:" << tp.time_since_epoch().count() << "s" << endl;

// 转化为ctime,打印输出时间点
time_t tt = chrono::system_clock::to_time_t(tp);


cout << ctime(&tt);


return 0;
}

时钟(代表当前系统的时间)

chrono中有三种时钟:system_clocksteady_clockhight_resolution_clock。 每一个clock类中都有确定的time_point,duration,Rep,Period类型。

system_clock是不稳定。因为时钟是可调的,即这种是完全自动适应本地账户的调节。这种调节可能造成的是,首次调用now()返回的时间要早于上次调用now()所返回的时间,这就违反了节拍频率的均匀分布。稳定闹钟对于超时的计算很重要,所以C++标准库提供了一个稳定时钟 std::chrono::steady_clock

std::chrono::hight_resolution_clock是标准库中提供的具有最小节拍周期(因此具有最高的精度的时钟)。

上文所说time_since_epoch(),以及将要介绍的now()函数的返回值都依赖于时钟的精度,测试时钟的精度的一种方式就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
#include <chrono>
using namespace std;

int main()
{
cout << "system clock :";
cout << chrono::system_clock::period::num << "/" << chrono::system_clock::period::den << "s" << endl;
cout << "steady clock :";
cout << chrono::steady_clock::period::num << "/" << chrono::steady_clock::period::den << "s" << endl;
cout << "hight resolution clock :";
cout << chrono::high_resolution_clock::period::num << "/" << chrono::high_resolution_clock::period::den << "s" << endl;



return 0;
}

windows系统的测试结果是system_clock的精度是100纳秒,而hight_resolution的精度是1纳秒,对于程序来说,一般毫秒级就够了,所以说chrono提供的时钟精度绰绰有余。

(1)成员函数static time_point now() noexcept; 用于获取系统的当前时间。

(2)由于各种time_point表示方式不同,chrono也提供了相应的转换函数 time_point_cast

1
2
3
4
template<class ToDuration, class Clock, class Duration>
time_point<Clock, ToDuration> time_point_cast (const time_point<Clock, Duration>& tp);

传一个要转换为的精度的duration模板参数和一个要转换的time_point参数 (用法见下面综合应用)

(3)其他成员函数:

1
2
to_time_t() time_point转换成time_t秒
from_time_t() 从time_t转换成time_point

综合应用:

输出当前时间,并计算当前的时间距离1970年1月1日00:00的毫秒数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <chrono>
#include <ctime>
using namespace std;


int main()
{

// 定义毫米级别的时钟类型
typedef chrono::time_point<chrono::system_clock, chrono::milliseconds> milliClockType;

// 获取当前时间点, window system_clock是100纳秒级别(不同系统可能会不一样),所以要转换
milliClockType tp = chrono::time_point_cast<chrono::milliseconds>(chrono::system_clock::now());

// 转换为ctime,用于打印显示时间
time_t tt = chrono::system_clock::to_time_t(tp);

cout << "now time is : " << ctime(&tt);

// 计算距离1970-1-1 00:00 的时间长度,因为当前时间点定义的精度为毫秒,所以输出的是毫秒
cout << "to 1970-1-1 00:00 ->" << tp.time_since_epoch().count() << "ms" << endl;


return 0;
}

将上面的程序中millisconds换成microseconds或者更小的单位,便可达到微妙,甚至更高的精度。

多谢您的大力支持