1.说明

std::choro::duration是C++11引入的一个用于计算时间滴答周期的类,与之配合使用的是std::ratio类,该类是一个分数类,为精确表示分数提供了一个方式。

2.std::ratio

我们知道,对于计算机来说,使用浮点类型是会有精度缺失的。所以std::ratio类便提供了分子和分母,

1
2
3
4
template<
std::intmax_t Num,
std::intmax_t Denom = 1
> class ratio;

该类的模板定义如下,Num是分子,Denom是分母。不传入分母的时候,分母默认为1。

假设我们需要表示1秒的千分之一(即一毫秒)就可以用如下方式来定义一个ratio

1
typedef ratio<1,1000> milli;

代表1/1000这个分数;

cpp中自带的几个ratio

为了方便标识单位之间的差距(单位换算),cpp中预定义了一些常用的ratio

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef ratio<1,       1000000000000000000> atto;
typedef ratio<1, 1000000000000000> femto;
typedef ratio<1, 1000000000000> pico;
typedef ratio<1, 1000000000> nano;
typedef ratio<1, 1000000> micro;
typedef ratio<1, 1000> milli;
typedef ratio<1, 100> centi;
typedef ratio<1, 10> deci;
typedef ratio< 10, 1> deca;
typedef ratio< 100, 1> hecto;
typedef ratio< 1000, 1> kilo;
typedef ratio< 1000000, 1> mega;
typedef ratio< 1000000000, 1> giga;
typedef ratio< 1000000000000, 1> tera;
typedef ratio< 1000000000000000, 1> peta;
typedef ratio< 1000000000000000000, 1> exa;

我们可以用类作用域符号::来访问到这些比例的分子和分母。

1
2
std::cout << std::centi::num << "/" << std::centi::den << std::endl;
// 运行结果为 1/100

3.std::chrono::duration

这个类是用来表示一个时间的周期/长度的,其基于1s为基本单位,用ratio来表示和1s相比的偏移量。

在cppreference上的介绍如下

Class template std::chrono::duration represents a time interval.
It consists of a count of ticks of type Rep and a tick period, where the tick period is a compile-time rational constant representing the number of seconds from one tick to the next.
The only data stored in a duration is a tick count of type Rep. If Rep is floating point, then the duration can represent fractions of ticks. Period is included as part of the duration’s type, and is only used when converting between different durations.

该类的定义如下

1
2
3
4
template<
class Rep,
class Period = std::ratio<1>
> class duration;

其中Rep是一个变量类型,可以传入int、float、double等类型;

Period是一个std::ratio,可以传入基于一秒的比例,这个比例就代表了时间的单位;

cpp中自带的几个chrono

为了方便我们使用,cpp中自带了几个用于表示时间的chrono的定义

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
27
28
29
30
31
    /// nanoseconds
using nanoseconds = duration<_GLIBCXX_CHRONO_INT64_T, nano>;

/// microseconds
using microseconds = duration<_GLIBCXX_CHRONO_INT64_T, micro>;

/// milliseconds
using milliseconds = duration<_GLIBCXX_CHRONO_INT64_T, milli>;

/// seconds
using seconds = duration<_GLIBCXX_CHRONO_INT64_T>;

/// minutes
using minutes = duration<_GLIBCXX_CHRONO_INT64_T, ratio< 60>>;

/// hours
using hours = duration<_GLIBCXX_CHRONO_INT64_T, ratio<3600>>;

#if __cplusplus > 201703L
/// days
using days = duration<_GLIBCXX_CHRONO_INT64_T, ratio<86400>>;

/// weeks
using weeks = duration<_GLIBCXX_CHRONO_INT64_T, ratio<604800>>;

/// years
using years = duration<_GLIBCXX_CHRONO_INT64_T, ratio<31556952>>;

/// months
using months = duration<_GLIBCXX_CHRONO_INT64_T, ratio<2629746>>;
#endif // C++20

代码里面出现的宏定义其实就是int64_t的别名。

1
# define _GLIBCXX_CHRONO_INT64_T int64_t

这些变量之前在学习C++线程休眠的时候就已经遇到过了,从中也能看到std::ratio在chrono中代表的比例关系,比如一小时是3600秒,那么就需要传入比例std::ratio<3600,1>,上面的代码里面省略了1是因为ratio的分母缺省值就是1。

代码示例1

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

int main()
{
typedef std::chrono::duration<int, std::ratio<1, 100000000>> shakes;
typedef std::chrono::duration<int, std::centi> jiffies;
typedef std::chrono::duration<float, std::ratio<12096,10000>> microfortnights;
typedef std::chrono::duration<float, std::ratio<3155,1000>> nanocenturies;

std::chrono::seconds sec(1);

std::cout << "1 second is:\n";

std::cout << std::chrono::duration_cast<shakes>(sec).count()
<< " shakes\n";
std::cout << std::chrono::duration_cast<jiffies>(sec).count()
<< " jiffies\n";
std::cout << std::chrono::duration_cast<microfortnights>(sec).count()
<< " microfortnights\n";
std::cout << std::chrono::duration_cast<nanocenturies>(sec).count()
<< " nanocenturies\n";
return 0;
}

上面的代码示例运行结果如下

1
2
3
4
5
6
> g++ test.cpp -o test -std=c++17 && ./test
1 second is:
100000000 shakes
100 jiffies
0.82672 microfortnights
0.316957 nanocenturies

我们能看到,第一个里面传入的比例是1/100000000,这就代表1秒包含了100000000个shakes,假设我们需要一个休眠1/100000000秒的操作,就可以通过std::this_thread::sleep_for和这个shakes变量来进行精确的定时休眠。

代码示例2

让当前线程休眠100毫秒,有几种不同的方式,都是等价的。

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

int main()
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// or
std::this_thread::sleep_for(std::chrono::duration<long long, std::milli>(100));
// or
// typedef ratio<1, 1000> milli;
std::this_thread::sleep_for(std::chrono::duration<long long, std::ratio<1, 1000> >(100));
}

4.std::chrono::duration_cast

为了方便在不同的时间单位之间进行转换,CPP提供了一个特殊的cast来进行处理

1
std::chrono::duration_cast<目标类型>(源类型对象);

比如可以用下面的代码将3秒转换为毫秒,注意圆括号里面的传参是源类型的一个对象,不能只传入类型(那样就不是变量类型转换了)

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

int main()
{
std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::minutes(3));
std::cout << "3 minutes equals to " << ms.count() << " milliseconds\n";

}

运行结果如下

1
2
> g++ test.cpp -o test -std=c++17 && ./test
3 minutes equals to 180000 milliseconds

The end

基本要了解的内容就这些了。

参考文档