繁体   English   中英

stable_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,它以毫秒为单位返回时间戳。

现在,当我打印出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 机器上运行此代码。

以相反的顺序回答问题:

用外行术语来说, steady_clocksystem_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();

唉,不管怎样,这仍然不能编译! 错误消息应该说明在nowdh_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 除外)。
  • 拒绝使用转换因子(如 1000)。
  • 与它争论直到它编译。

如果您在以上四点中取得成功,您很可能不会遇到任何运行时错误(但您会得到应有的编译时错误)。

第一件事

您看到正值的原因是无符号整数环绕。 试试这个,看看:

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

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

稳定与系统时间

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

类 std::chrono::steady_clock 表示单调时钟。 这个时钟的时间点不会随着物理时间的向前移动而减少。 此时钟与挂钟时间无关,最适合测量间隔。

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

  1. stable_clock 使用系统启动时间作为它的纪元,system_clock 使用 1970-1-1 00:00 作为它的纪元,所以它们之间没有办法做任何数学运算,这是没有意义的。

  2. 在 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM