system_clock::now()和time()时间函数混用带来的踩坑经历

前言

时间是一个可怕的东西,听说能用来杀猪。在编程世界中,时间也控制着一个维度,常常伴随着程序运行而流逝,有时也会影响着程序的运行的逻辑,所以在程序中处理时间时还是要仔细一些,最近连续踩坑,总结一下给自己提个醒,有些逻辑还是需要抱着怀疑的态度去看待。

时间函数混用

我们在写一个小程序时基本不会去混用时间函数,比如只用 time(NULL) 去控制时间,或者只使用 chrono::system_clock::now() 来记录时间消耗,关于 chrono 的用法,之前简单总结过,可传送至 C++11中的时间库std::chrono

但是当程序变得复杂起来,这个时间函数混用的高压线还是有可能触碰到的,当程序逻辑对时间要求越发精确时,混用所带来的后果将越发严重。在此记录一个结果:连续调用 time(NULL)chrono::system_clock::now() 两个函数得到的时间戳可能是不同的。

可能你会说,函数是先后调用的,肯定是不同的,后面的函数调用时的时间戳要比前面的大,但事实却是两个函数所取得的时间戳大小不确定,可能是第一个函数的时间戳比较大,也可能是第二个时间戳更大一些。

测试的例子

下面展示一段代码,先后调用两个时间函数,打印所获得的时间戳,可以看看有什么特点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#include <iostream>
#include <chrono>
using namespace std;

int main()
{
int64_t t1, t2;
while (true) {
t1 = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
t2 = time(0);

if (t1/1000 != t2) cout << t1 << " " << t2 << endl;
}

return 0;
}

编译运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
albert@home-pc:testtime$ g++ testtime.cpp -std=c++11
albert@home-pc:testtime$ ./a.out
1607779917993 1607779918
1607779957999 1607779958
1607780080001 1607780079
1607780103001 1607780102
1607780150001 1607780149
1607780202001 1607780201
1607780327999 1607780328
1607780440001 1607780439
...

运行之后很快就出现了一些不一致,对比可以发现,两个时间戳一个是毫秒,一个是秒,同时把单位转化成秒来比较时,两者大小不定,从仅有的这几行结果来看,最大的误差是7毫秒。

再加一个时间函数

除了上面提到的两个函数,还有一个 gettimeofday() 函数也是在获取时间时常常使用的,把它也放到测试函数中对比一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdint.h>
#include <sys/time.h>
#include <time.h>
#include <iostream>
#include <chrono>
using namespace std;

int main()
{
int64_t t1, t2, t = 0;
struct timeval tv;
while (true) {
t1 = chrono::duration_cast<chrono::milliseconds>(chrono::system_clock::now().time_since_epoch()).count();
t2 = time(0);
gettimeofday(&tv, NULL);

if (t1/1000 != t2 || t2 != tv.tv_sec)
if (t != t1) cout << t1 << " " << t2 << " " << tv.tv_sec << "," << tv.tv_usec << endl;

t = t1;
}
return 0;
}

运行后查看结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
albert@DESKTOP-6746UC3:/mnt/d/data/cpp/testtime$ g++ testtime.cpp --std=c++11
albert@DESKTOP-6746UC3:/mnt/d/data/cpp/testtime$ ./a.out
1607876993000 1607876992 1607876993,2
1607876994000 1607876993 1607876994,3
1607876995000 1607876994 1607876995,3
1607876996000 1607876995 1607876996,2
1607876997000 1607876996 1607876997,1
1607876998000 1607876997 1607876998,2
1607876999000 1607876998 1607876999,2
1607877000000 1607876999 1607877000,3
1607877001000 1607877000 1607877001,1
1607877002000 1607877001 1607877002,3
1607877003000 1607877002 1607877003,2
1607877004000 1607877003 1607877004,2
1607877005000 1607877004 1607877005,1
1607877006000 1607877005 1607877006,3
1607877007000 1607877006 1607877007,2
1607877008000 1607877007 1607877008,11
1607877009000 1607877008 1607877009,3
...

真是各不相同,这要是在发射火箭时混用两个时间函数,那估计探月卫星就凉凉了……

总结

  • 常用来获取时间戳的函数有 time()chrono::system_clock::now()gettimeofday()
  • 时间函数不要混用,否则会给精密计算带来巨大的麻烦,造成计算结果的不可控
  • 测试发现 chrono::system_clock::now()gettimeofday() 时间非常接近,有微秒级别的误差,但也不建议混用

==>> 反爬链接,请勿点击,原地爆炸,概不负责!<<==

有的人走了只留下一撮灰烬,有的人离开却千古留名,但在时间长河中就是那么一瞬,意义何在,有差吗?

2020-12-14 00:12:01

Albert Shi wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客