[英]Difference between std::system_clock and std::steady_clock?
[英]Difference between steady_clock vs system_clock?
我试图通过查看数据的时间戳来查看我的数据是否为 120 秒,所以我有以下代码:
uint64_t now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
bool is_old = (120 * 1000 < (now - data_holder->getTimestamp()));
在上面的代码中data_holder->getTimestamp()
是 uint64_t,它以毫秒为单位返回时间戳。
现在,当我打印出now
变量值时,我看到了这个10011360
,当我打印出data_holder->getTimestamp()
值时,它是1437520382241
所以现在和数据持有者时间戳的差异应该是负数,对吧? 为什么它会像下面的日志中显示的那样积极?
2015-07-21 16:13:02,530 WARN 0x7f35312d1700 data_check - now value: 10011360 , data holder timestamp: 1437520382241 , difference: 18446742636199180735
我上面的代码看起来正确吗? 从上面的数据持有者时间戳来看,它看起来不是 120 秒的旧数据,所以我觉得我的代码有问题? 因为如果我将该数据持有者时间戳转换为实际时间(使用纪元转换器),然后将其与日志时间进行比较,如上所示,它几乎相同。
我正在使用steady_clock
如上所示。 我需要在这里使用system_clock
吗? 用外行术语来说, steady_clock
与system_clock
之间有什么区别。 我在 Ubuntu 14.04 机器上运行此代码。
以相反的顺序回答问题:
用外行术语来说,
steady_clock
与system_clock
之间有什么区别。
如果你手里拿着一个system_clock
,你会称它为watch ,它会告诉你现在几点了。
如果你手里拿着一个steady_clock
,你会称它为秒表,它会告诉你某人跑了一圈有多快,但它不会告诉你现在steady_clock
了。
如果必须的话,你可以用你的手表给某人跑一圈计时。 但是,如果您的手表(如我的)定期与另一台机器(例如 Boulder CO 的原子钟)通信以将自身更正为当前时间,则可能会在计时该圈时出现小错误。 秒表不会犯那个错误,但它也不能告诉你正确的当前时间是什么。
我上面的代码看起来正确吗?
不。即使它给了你合理的答案,我也不会说它是对的。 不要难过,这是很多人在使用<chrono>
库时犯的初学者错误。
我对<chrono>
库遵循一个简单的规则。 该规则实际上并不完全正确(因此它是一个指导方针)。 但它足以纠正成为几乎总是遵循的准则:
不要使用
count()
。
一个推论:
不要使用
time_since_epoch()
。
<chrono>
库是围绕类型安全系统设计的,旨在保护您免受单位转换错误的影响。 如果您不小心尝试了不安全的转换,则会在编译时捕获错误(而不是运行时错误)。
成员函数count()
和time_since_epoch()
是这种类型安全系统的“逃生舱”……仅在紧急情况下使用。 当(例如)委员会忽略为您提供完成<chrono>
类型的工作(例如 I/O)所需的所有工具时,就会出现这种紧急情况,或者例如需要与其他一些计时 API 接口通过整数。
检查您的代码和其他代码以使用count()
和time_since_epoch()
并仔细检查这些函数的每次使用:有什么方法可以重写代码以消除它们的使用?
查看代码的第一行:
uint64_t now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
now
是一个time_point
(来自steady_clock
)。 它的单位是milliseconds
,但此时我不相信这些单位很重要。 重要的是now
是从time_point
检索的steady_clock
:
auto now = steady_clock::now();
你的第二行更复杂:
bool is_old = (120 * 1000 < (now - data_holder->getTimestamp()));
让我们从data_holder->getTimestamp()
:如果您可以修改getTimestamp()
,您应该修改它以返回time_point
而不是uint64_t
。 为此,您必须知道正确的单位(您所做的 - 毫秒),并且您必须知道正确的纪元。 纪元是衡量毫秒数的时间点。
在这种情况下,1437520382241ms 大约是 45.6 年。 假设这是最近的时间戳,45.6 年前非常接近 1970-01-01。 事实证明, system_clock()
每个实现都使用 1970-01-01 作为它的纪元(尽管每个实现从这个纪元开始计算不同的单位)。
因此,要么修改getTimestamp()
返回一个time_point<system_clock, milliseconds>
,或包裹的返回getTimestamp()
与time_point<system_clock, milliseconds>
:
auto dh_ts = system_clock::time_point{milliseconds{data_holder->getTimestamp()}};
现在你的第二行是:
bool is_old = (120 * 1000 < (now - dh_ts));
另一个很好的指导方针:
如果您在
<chrono>
代码中看到转换系数,那么您就做错了。<chrono>
生活做了转换你。
bool is_old = (minutes{2} < (now - dh_ts));
下一步是风格化的,但现在你的代码足够简单,可以去掉多余的括号,如果这对你有吸引力的话:
bool is_old = minutes{2} < now - dh_ts;
如果您能够修改getTimestamp()
以返回类型安全值,则此代码也可能如下所示:
bool is_old = minutes{2} < now - data_holder->getTimestamp();
唉,不管怎样,这仍然不能编译! 错误消息应该说明在now
和dh_ts
之间没有有效的operator-()
。
这是类型安全系统,它可以帮助您避免运行时错误!
的问题是, time_point
从s system_clock
不能从减去time_point
期从steady_clock
(因为两个具有不同的时期)。 所以你必须切换到:
auto now = system_clock::now();
把它们放在一起:
#include <chrono>
#include <cstdint>
#include <memory>
struct DataHolder
{
std::chrono::system_clock::time_point
getTimestamp()
{
using namespace std::chrono;
return system_clock::time_point{milliseconds{1437520382241}};
}
};
int
main()
{
using namespace std;
using namespace std::chrono;
auto data_holder = std::unique_ptr<DataHolder>(new DataHolder);
auto now = system_clock::now();
bool is_old = minutes{2} < now - data_holder->getTimestamp();
}
在 C++14 中,最后一行可以更简洁一点:
bool is_old = 2min < now - data_holder->getTimestamp();
总之:
count()
(I/O 除外)。time_since_epoch()
(I/O 除外)。如果您在以上四点中取得成功,您很可能不会遇到任何运行时错误(但您会得到应有的编译时错误)。
您看到正值的原因是无符号整数环绕。 试试这个,看看:
std::cout << static_cast <uint64_t> (-1) << std::endl;
getTimestamp()
返回的值是预期的吗? 如果没有,如果没有看到getTimestamp()
的实现,就很难看出哪里出了问题。 看起来时间戳不是使用相同的时钟测量的。
稳定时钟最适合测量时间间隔。 从cppreference.com引用:
类 std::chrono::steady_clock 表示单调时钟。 这个时钟的时间点不会随着物理时间的向前移动而减少。 此时钟与挂钟时间无关,最适合测量间隔。
与 system_clock 不同,它不是单调的(即,如果用户更改主机上的时间,时间会减少。)
stable_clock 使用系统启动时间作为它的纪元,system_clock 使用 1970-1-1 00:00 作为它的纪元,所以它们之间没有办法做任何数学运算,这是没有意义的。
在 2 个无符号整数之间做任何减法之前,请确保被减数大于被减数。
第一个问题,负整数会隐式转换uint64_t类型的整数,变成一个巨大的正整数。
第二个问题,system_clock 是一个系统范围的实时时钟,如果你修改了 system_clock 的返回时间改变了系统时间。 stable_clock 是物理时间,因此您无法更改它。
也许,最显着的区别是std::chrono:system_clock
的起始时刻是 01.01.1970,即所谓的 UNIX-epoch。 另一方面,对于std::chrono::steady_clock
通常是 PC 的启动时间,它最适合测量间隔。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.