以相反的顺序回答问题:
steady_clock 与 system_clock 之间的区别是什么?
条款。
如果你手里拿着system_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 是从steady_clock 检索到的time_point:
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-()。
这是一种类型安全系统,可帮助您避免运行时错误!
问题是system_clock 中的time_points 不能从steady_clock 中的time_points 中减去(因为两者具有不同的时期)。所以你必须切换到:
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 除外)。
- 拒绝使用转换系数(例如 1000)。
- 与它争论,直到它编译为止。
如果您在上述四点上取得成功,您很可能不会遇到任何运行时错误(但您会得到应有的编译时错误)。