基本概念
C++11中引入了一个新的关于时间的库,所有内容都包括在std::chrono
中,使用时直接引用头文件#include<chrono>
和命名空间std::chrono
即可。
std::chrono
库中主要有3个概念,时钟(Clocks)、时间段(Duration)、时间点(timePoint)。
时钟(Clocks)
顾名思义,就是和计时的相关类。主要就有3个,分别是:
-
系统时钟(system_clock), 和操作系统的系统时间同步的时钟,一般是unix时间,即从1970年1月1日到当前系统时间的时间间隔。例如系统时间是1970年1月2日23:59:59,那么返回的值即为$246060=86400$秒。如果系统时间发生改变,相应的值也会发生改变。一般就是用来读取当前的系统时间。
-
单调时钟(steady_clock), 就类似秒表,每一次调用返回的值都会大于上一次调用的值,和系统的时间无关。用于程序计时尽量用steady_clock,可以防止在不同PC上运行导致的返回时间不准确。
-
高精度时钟(high_resolution_clock), 提供拥有最短计数周期的时钟,在某些编译器中可能是std::steady_clock或std::system_clock的别名,并且在不同编译器中的实现可能有巨大差异,应该尽量避免使用。
一般来说,我们使用的都是system_clock, steady_clock相比system_clock就少了两个静态成员函数:to_time_t
和from_time_t
。
下面是两个使用示例:
使用system_clock读取系统时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include <chrono> #include <ctime>
int main() { using namespace std::chrono;
duration<int, std::ratio<60*60*24>> oneDay{1}; system_clock::time_point today = system_clock::now(); system_clock::time_point tomorrow = today + oneDay;
std::time_t curTime = system_clock::to_time_t(today); std::cout << "Today is: " << ctime(&curTime);
std::time_t nextTime = system_clock::to_time_t(tomorrow); std::cout << "tomorrow is: " << ctime(&nextTime); }
|
输出:
1 2
| Today is: Sat Jan 20 01:46:56 2024 tomorrow is: Sun Jan 21 01:46:56 2024
|
记录函数的运行时间一般使用steady_clock:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <iostream> #include <chrono> #include <ctime>
int main() { using namespace std::chrono;
steady_clock::time_point start = steady_clock::now(); for (int i = 0;i < 1000;i ++) std::cout << '*'; std::cout << std::endl;
steady_clock::time_point end = steady_clock::now();
duration<double, std::ratio<1>> period = duration_cast<duration<double>>(end - start); std::cout << "Took " << period.count() << " seconds" << std::endl; }
|
输出:
注意,steady_clock没有to_time_t
和from_time_t
,只能提供基本计时操作。
时间段(Durations)
顾名思义就是表示一段持续的时间,比如1s、1min、1day等,需要指定单位,所以Duration需要提供一个模板来指定。
他的定义如下:
1
| template<class Rep, class Period = std::ratio<1>> class duration;
|
其中Period
就是用来表示时间段的单位,比如天、分钟、秒等。Rep
就是实际用来存储数据类型。比如10秒,则Period
就为std::ratio<1>
, Rep
就为int
,数值为10。如果要表示10.0秒,则Rep
就为double
。一般
Period
类是std::ratio
的别名,也就是一个分数(比率),定义如下:
1
| template <intmax_t N, intmax_t D = 1> class ratio;
|
N表示分子,D表示分母,用秒作为基本单位。例如当N=1,D=1时候,就是1/1 = 1s,当N=1,D=1000时,就是1/1000 = 1ms,当N=60,D=1时,就是60/1 = 1min。
标准库定义了一些常见的时间段:
1 2 3 4 5 6
| using nanoseconds = duration<_GLIBCXX_CHRONO_INT64_T, ratio<1,1000000000>>; using microseconds = duration<_GLIBCXX_CHRONO_INT64_T, ratio<1, 1000000>>; using milliseconds = duration<_GLIBCXX_CHRONO_INT64_T, ratio<1, 1000>>; using seconds = duration<_GLIBCXX_CHRONO_INT64_T>;
using minutes = duration<_GLIBCXX_CHRONO_INT64_T, ratio<60, 1>>;
|
其他的参考:https://cplusplus.com/reference/chrono/duration/ ,懒得写了。
对于duration来说,一般有两种操作:
- 时间的增减。
duration重载了大多数运算符,可以直接进行运算:
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
| #include <iostream> #include <ratio> #include <chrono>
int main () {
std::chrono::duration<int> foo{}; std::chrono::duration<int> bar (10);
foo = bar; foo = foo + bar; ++foo; --bar; foo *= 2; foo /= 3; bar += ( foo % bar );
std::cout << std::boolalpha; std::cout << "foo==bar: " << (foo==bar) << std::endl; std::cout << "foo: " << foo.count() << std::endl; std::cout << "bar: " << bar.count() << std::endl;
return 0; }
|
输出:
1 2 3
| foo==bar: true foo: 14 bar: 14
|
- 不同单位时间段的转换
可以使用duration_cast
函数来转换:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include <iostream> #include <chrono>
int main () { std::chrono::seconds s (1); std::chrono::milliseconds ms = std::chrono::duration_cast<std::chrono::milliseconds> (s); std::cout << "ms: " << ms.count() << std::endl; std::cout << "s: " << s.count() << std::endl;
ms += std::chrono::milliseconds(2500);
s = std::chrono::duration_cast<std::chrono::seconds> (ms);
std::cout << "ms: " << ms.count() << std::endl; std::cout << "s: " << s.count() << std::endl;
return 0; }
|
输出:
1 2 3 4
| ms: 1000 s: 1 ms: 3500 s: 3
|
时间点(time_point)
时间点表示一个确切的时间,例如:2024年1月20日1:49:53。
时钟(clocks)返回的值就是一个时间点,两个时间点之间的差值就是一段时间(duration)。
时间点的定义如下:
1
| template<class Clock, class Duration = typename Clock::duration> class time_point;
|
可以看出,标准库将时间点定义为:在基准时钟的起始时间加上一个时间段,一次来表示一个时间点。所以时间点都是基于基准时钟的起始时间来的。
常用的函数有:
time_since_epoch()
: 返回基于起始时间的时间段。
time_point_cast()
: 用来将时间点转换为基于同一个时钟,但为不同单位的时间点。
一般使用时间点的场景就是类似clock的那种,记录两个时间点然后计时,或者转换为时间戳什么的。
时间点加减时间段返回一个新的时间点,时间点加减一个时间点返回一个时间段,和常识都一样,还是挺好理解。