steady_clock和system_clock之间的区别?

use*_*349 19 c++ timestamp c++11

我试图通过查看数据的时间戳来查看我的数据是否是120秒旧,所以我有以下代码:

uint64_t now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
bool is_old = (120 * 1000 < (now - data_holder->getTimestamp()));
Run Code Online (Sandbox Code Playgroud)

在上面的代码中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
Run Code Online (Sandbox Code Playgroud)

我上面的代码是否正确?从上面的数据持有者时间戳,它看起来不是120秒的旧数据,所以我觉得我的代码有问题吗?因为如果我将数据持有者时间戳转换为实际时间(使用纪元转换器),然后将其与日志时间进行比较,如上所示,它几乎相同.

我正在使用steady_clock如上所示.我需要在system_clock这里使用吗?在外行人的条款中,steady_clockvs 之间有什么区别system_clock.我在Ubuntu 14.04框上运行此代码.

How*_*ant 43

以相反的顺序回答问题:

在外行人的条款中,steady_clockvs 之间有什么区别system_clock.

如果你握system_clock在手中,你会把它称为手表,它会告诉你现在是什么时候.

如果你手里拿着一个steady_clock,你会把它称为秒表,它会告诉你有多快跑一圈,但它不会告诉你现在几点钟.

如果你不得不这样做,你可以指望有人用你的手表跑一圈.但是,如果你的手表(像我的手机)定期与另一台机器(例如博尔德公司的原子钟)进行通信,以便将其自身纠正到当前时间,那么它可能会在一圈时间内出现轻微错误.秒表不会犯这个错误,但它也无法告诉你正确的当前时间是什么.

我上面的代码看起来是否正确?

不,即使它给了你合理的答案,我也不会说它是对的.不要心疼,这是许多人对<chrono>图书馆犯的初学错误.

我跟随<chrono>图书馆有一个简单的规则.该规则实际上并不完全正确(因此它是一个指导原则).但它足够接近于纠正成为几乎总是遵循的指导方针:

不要用count().

还有一个推论:

不要用time_since_epoch().

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

成员函数count()time_since_epoch()有"逃生舱"出这种类型的安全系统......只有在紧急情况下使用.当(例如)委员会忽略为您提供完成工作所需的所有工具(例如I/O)<chrono>以及需要通过整数与其他时序API连接时,就会出现这种紧急情况.

检查你的代码和其他对使用count()time_since_epoch()并仔细检查每一处使用这些功能:是否有任何方式的代码可以改写,以消除它们的使用?

查看代码的第一行:

uint64_t now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
Run Code Online (Sandbox Code Playgroud)

nowtime_point(来自steady_clock).单位是milliseconds,但在这个时候我不相信单位是重要的.最重要的是那now是一个time_point从数据库中检索steady_clock:

auto now = steady_clock::now();
Run Code Online (Sandbox Code Playgroud)

你的第二行更复杂:

bool is_old = (120 * 1000 < (now - data_holder->getTimestamp()));
Run Code Online (Sandbox Code Playgroud)

让我们开始data_holder->getTimestamp():如果你可以修改getTimestamp(),你应该修改它来返回time_point而不是a 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()}};
Run Code Online (Sandbox Code Playgroud)

现在你的第二行是:

bool is_old = (120 * 1000 < (now - dh_ts));
Run Code Online (Sandbox Code Playgroud)

另一个好的准则:

如果您在<chrono>代码中看到转换因素,那么您做错了. 你做转换<chrono> 活着.

bool is_old = (minutes{2} < (now - dh_ts));
Run Code Online (Sandbox Code Playgroud)

下一步是风格,但现在你的代码很简单,可以摆脱多余的括号,如果这是吸引你的东西:

bool is_old = minutes{2} < now - dh_ts;
Run Code Online (Sandbox Code Playgroud)

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

bool is_old = minutes{2} < now - data_holder->getTimestamp();
Run Code Online (Sandbox Code Playgroud)

唉,无论哪种方式,这仍然无法编译!错误消息应该说明operator-()now和之间没有效的行dh_ts.

这是类型安全系统,可以帮助您避免运行时错误!

问题是time_points from system_clock不能从time_points中减去steady_clock(因为两者有不同的时期).所以你必须切换到:

auto now = system_clock::now();
Run Code Online (Sandbox Code Playgroud)

把它们放在一起:

#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();
}
Run Code Online (Sandbox Code Playgroud)

在C++ 14中,最后一行可以更简洁:

    bool is_old = 2min < now - data_holder->getTimestamp();
Run Code Online (Sandbox Code Playgroud)

综上所述:

  • 拒绝使用count()(I/O除外).
  • 拒绝使用time_since_epoch()(I/O除外).
  • 拒绝使用转换因子(例如1000).
  • 争论它直到它编译.

如果你在上述四点中取得成功,你很可能不会遇到任何运行时错误(但是你会得到公平的编译时错误).

  • @AngelusMortis:如果没有更多关于您要解决的更大问题的背景信息,这个问题很难回答。然而我的总体观点是“time_since_epoch()”和“count()”相当于强制转换。它们是强类型系统的逃生口。初学者往往会过度使用这些函数,从而导致类型系统无能为力。尽力保持在“&lt;chrono&gt;”类型系统内,编译器将在编译时检测到您的逻辑错误(而不是编译时逻辑错误并导致运行时错误),您将获得奖励。 (3认同)

小智 6

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

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