steady_clock 与 system_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 其中 returns 时间戳以毫秒为单位。

现在当我打印出 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_clocksystem_clock 之间有什么区别?我是 运行 Ubuntu 14.04 盒子上的这个代码。

要事第一

您看到正值的原因是无符号整数回绕。试试看:

std::cout << static_cast <uint64_t> (-1) << std::endl;

getTimestamp()返回的值是否符合预期?如果不是,在没有看到 getTimestamp() 的实现的情况下,很难看出哪里出了问题。看起来时间戳不是使用相同的时钟测量的。

稳定与系统时间

稳定的时钟最适合测量时间间隔。引用自cppreference.com

Class std::chrono::steady_clock represents a monotonic clock. The time points of this clock cannot decrease as physical time moves forward. This clock is not related to wall clock time, and is best suitable for measuring intervals.

与 system_clock 不同,它不是单调的(也就是说,如果用户更改主机上的时间,时间会减少。)

倒序回答问题:

What is the difference between steady_clock vs system_clock in layman terms.

如果你手里拿着一个system_clock,你会称它为手表,它会告诉你现在几点了。

如果你手里拿着一个 steady_clock,你会称它为 秒表 ,它会告诉你某人有多快 运行一圈,但它不会告诉你现在几点了。

如果有必要,您可以用您的手表为某人 运行 跑一圈计时。但是,如果您的手表(如我的)定期与另一台机器(例如 Boulder CO 的原子钟)通信以将自身校正到当前时间,则它可能会在计时那一圈时犯小错误。秒表不会犯那个错误,但它也不能告诉你正确的当前时间是多少。

Does my above code look right?

没有。即使它给了你合理的答案,我也不会说它是对的。不要难过,这是很多人在 <chrono> 库中犯的初学者错误。

我在 <chrono> 库中遵循一个简单的规则。该规则实际上并不完全正确(因此它是一个指南)。但它已经足够接近,可以作为几乎始终遵循的准则进行更正:

Don't use count().

一个推论:

Don't use time_since_epoch().

<chrono> 库是围绕 类型安全 系统设计的,旨在保护您免受单位转换错误的影响。如果您不小心尝试了不安全的转换,错误会在编译时被捕获(而不是 运行 时间错误)。

成员函数 count()time_since_epoch() "escape hatches" 不属于此类型安全系统...仅在紧急情况下使用。当(例如)委员会忽视为您提供完成 <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(),应该修改成return一个time_point,而不是一个uint64_t。为此,您将必须知道正确的单位(您所做的 - 毫秒), 您将必须知道正确的纪元。纪元是你的毫秒数的测量时间点。

在这种情况下,1437520382241ms 大约是 45.6 年。假设这是最近的时间戳,45.6 年前非常接近 1970-01-01。事实证明,system_clock() 的每个实现都使用 1970-01-01 作为它的纪元(尽管每个实现从这个纪元计算不同的单位)。

所以要么把getTimestamp()修改成return一个time_point<system_clock, milliseconds>,要么把getTimestamp()的return换成time_point<system_clock, milliseconds>:

auto dh_ts = system_clock::time_point{milliseconds{data_holder->getTimestamp()}};

现在你的第二行是:

bool is_old = (120 * 1000 < (now - dh_ts));

另一个很好的指导方针:

If you see conversion factors in your <chrono> code, you're doing it wrong. <chrono> lives for doing the conversions for you.

bool is_old = (minutes{2} < (now - dh_ts));

下一步是风格化的,但现在您的代码足够简单,可以去掉多余的括号,如果您喜欢的话:

bool is_old = minutes{2} < now - dh_ts;

如果您能够将 getTimestamp() 修改为 return 类型安全值,则此代码也可能如下所示:

bool is_old = minutes{2} < now - data_holder->getTimestamp();

唉,不管怎样,这个仍然不编译!错误消息应该说明 nowdh_ts 之间没有有效的 operator-()

This is the type-safety system coming in to save you from run time errors!

问题是system_clocktime_points不能从steady_clocktime_points中减去(因为两者有不同的epochs)。所以你必须切换到:

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)。
  • 争论它直到它编译。

如果您在上述四点上都成功了,您很可能不会遇到任何 运行 时间错误(但您会遇到相当一部分编译时间错误)。

第一题,负整数会隐式转换uint64_t类型的整数,变成一个巨大的正整数

第二个问题,system_clock是一个系统范围的实时时钟,如果你修改系统时间作为system_clock的return时间改变。 steady_clock 是物理时间,因此您无法更改它。

  1. steady_clock以系统启动时间为纪元,system_clock 使用 1970-1-1 00:00 作为纪元,因此无法进行任何数学运算 在他们之间,这是没有意义的。

  2. 在对 2 个无符号整数进行减法运算之前,请 确保被减数大于减数。

也许,最显着的区别是 std::chrono:system_clock 的开始时间是 01.01.1970,即所谓的 UNIX 纪元。另一方面,std::chrono::steady_clock 通常是您的 PC 的启动时间,最适合测量时间间隔。