C++多线程之处理日期和时间的 chrono 库

1.简介

C++11 中提供了日期和时间相关的库 chrono,通过 chrono 库可以很方便地处理日期和时间,为程序的开发提供了便利。
chrono 库主要包含三种类型的类:

时间间隔duration、时钟clocks、时间点time point。

2.时间间隔 duration

duration表示一段时间间隔,用来记录时间长度,可以表示几秒、几分钟、几个小时的时间间隔。

  • duration 的原型如下:
// 定义于头文件 <chrono>
template<
    class Rep,
    class Period = std::ratio<1>
> class duration;
  • Rep:这是一个数值类型,表示时钟数(周期)的类型(默认为整形)。
    若 Rep 是浮点数,则 duration 能使用小数描述时钟周期的数目。
  • Period:表示时钟的周期,它的原型如下:
// 定义于头文件 <ratio>
template<
    std::intmax_t Num,
    std::intmax_t Denom = 1

> class ratio;
  • ratio 类表示每个时钟周期的秒数,其中第一个模板参数 Num代表分子,Denom代表分母,该分母值默认为 1

因此,ratio代表的是一个分子除以分母的数值,比如:ratio<2> 代表一个时钟周期是 2 秒,ratio<60 > 代表一分钟,ratio<60*60 > 代表一个小时,ratio<60*60*24 > 代表一天。而 ratio<1,1000 > 代表的是 1/1000 秒,也就是 1 毫秒,ratio<1,1000000 > 代表一微秒,ratio<1,1000000000 > 代表一纳秒。

  • 为了方便使用,在标准库中定义了一些常用的时间间隔,比如:时、分、秒、毫秒、微秒、纳秒,它们都位于 chrono 命名空间下,定义如下:
类型	定义
纳秒:std::chrono::nanoseconds	duration<Rep*/* 至少 64 位的有符号整数类型 */*, std::nano>
微秒:std::chrono::microseconds	duration<Rep*/* 至少 55 位的有符号整数类型 */*, std::micro>
毫秒:std::chrono::milliseconds	duration<Rep*/* 至少 45 位的有符号整数类型 */*, std::milli>
秒: std::chrono::seconds	duration<Rep*/* 至少 35 位的有符号整数类型 */*>
分钟:std::chrono::minutes	duration<Rep*/* 至少 29 位的有符号整数类型 */*, std::ratio<60>>
小时:std::chrono::hours	duration<Rep*/* 至少 23 位的有符号整数类型 */*, std::ratio<3600>>
  • 注意:到 hours 为止的每个预定义时长类型至少涵盖 ±292 年的范围。

  • duration 类的构造函数原型如下:

// 1. 拷贝构造函数
duration( const duration& ) = default;

// 2. 通过指定时钟周期的类型来构造对象
template< class Rep2 >
constexpr explicit duration( const Rep2& r );

// 3. 通过指定时钟周期类型,和时钟周期长度来构造对象
template< class Rep2, class Period2 >
constexpr duration( const duration<Rep2,Period2>& d );
  • 为了更加方便的进行 duration 对象之间的操作,类内部进行了操作符重载:
    在这里插入图片描述

  • duration 类还提供了获取时间间隔的时钟周期数的方法 count (),函数原型如下:

constexpr rep count() const;

(1)duration类的构造

  • 通过构造函数构造时间间隔对象示例代码如下:

chrono 库中根据 duration 类封装了不同长度的时钟周期(也可以自定义),基于这个时钟周期再进行周期次数的设置就可以得到总的时间间隔了(时钟周期 * 周期次数 = 总的时间间隔)。

#include <chrono>
#include <iostream>
using namespace std;
int main()
{
	//h(1) 时钟周期为 1 小时,共有 1 个时钟周期,所以 h 表示的时间间隔为 1 小时
    chrono::hours h(1);                          // 一小时

	//ms(3) 时钟周期为 1 毫秒,共有 3 个时钟周期,所以 ms 表示的时间间隔为 3 毫秒
    chrono::milliseconds ms{ 3 };                // 3 毫秒

	//ks(3) 时钟周期为 1000 秒,一共有三个时钟周期,所以 ks 表示的时间间隔为 3000 秒
    chrono::duration<int, ratio<1000>> ks(3);    // 3000 秒
	
	//d3(3.5) 时钟周期为 1000 秒,时钟周期数量只能用整形来表示,但是此处指定的是浮点数,因此语法错误
    // chrono::duration<int, ratio<1000>> d3(3.5);  // error

	//dd(6.6) 时钟周期为默认的 1 秒,共有 6.6 个时钟周期,所以 dd 表示的时间间隔为 6.6 秒
    chrono::duration<double> dd(6.6);               // 6.6 秒

    // 使用小数表示时钟周期的次数
    //hz(3.5) 时钟周期为 1/30 秒,共有 3.5 个时钟周期,所以 hz 表示的时间间隔为 1/30*3.5 秒
    chrono::duration<double, std::ratio<1, 30>> hz(3.5);
}
  • eg:
#include <chrono>
#include <iostream>
int main()
{
//ms 时间单位为毫秒,初始化操作 ms{3} 表示时间间隔为 3 毫秒,一共有 3 个时间周期,每个周期为 1 毫秒
    std::chrono::milliseconds ms{3};         // 3 毫秒

//s 时间单位为微秒,初始化操作 2*ms 表示时间间隔为 6000 微秒,一共有 6000 个时间周期,每个周期为 1 微秒
    std::chrono::microseconds us = 2*ms;     // 6000 微秒

    // 时间间隔周期为 1/30 秒
//hz 时间单位为秒,初始化操作 hz(3.5) 表示时间间隔为 1/30*3.5 秒,一共有 3.5 个时间周期,每个周期为 1/30 秒 
    std::chrono::duration<double, std::ratio<1, 30>> hz(3.5);

    std::cout <<  "3 ms duration has " << ms.count() << " ticks\n"
              <<  "6000 us duration has " << us.count() << " ticks\n"
              <<  "3.5 hz duration has " << hz.count() << " ticks\n";       
}
  • 测试:
    在这里插入图片描述

  • eg:由于在 duration 类内部做了操作符重载,因此时间间隔之间可以直接进行算术运算,比如我们要计算两个时间间隔的差值,就可以在代码中做如下处理:

#include <iostream>
#include <chrono>
using namespace std;

int main()
{
    chrono::minutes t1(10);
    chrono::seconds t2(60);
    chrono::seconds t3 = t1 - t2;
    cout << t3.count() << " second" << endl;
}
  • 测试:
    在这里插入图片描述
  • 结论:

在上面的测试程序中,t1 代表 10 分钟,t2 代表 60 秒,t3 是 t1 减去 t2,也就是 60*10-60=540,这个 540 表示的时钟周期,每个时钟周期是 1 秒,因此两个时间间隔之间的差值为 540 秒

注意事项:duration 的加减运算有一定的规则,当两个 duration 时钟周期不相同的时候,会先统一成一种时钟,然后再进行算术运算,统一的规则如下:假设有 ratio<x1,y1> 和 ratio<x2,y2 > 两个时钟周期,首先需要求出 x1,x2 的最大公约数 X,然后求出 y1,y2 的最小公倍数 Y,统一之后的时钟周期 ratio 为 ratio<X,Y>

  • eg:对于分子 6,、9 最大公约数为 3,对于分母 7、5 最小公倍数为 35,因此推导出的时钟周期为 ratio<3,35>
#include <iostream>
#include <chrono>
using namespace std;

int main()
{
    chrono::duration<double, ratio<9, 7>> d1(3);
    chrono::duration<double, ratio<6, 5>> d2(1);
    // d1 和 d2 统一之后的时钟周期
    chrono::duration<double, ratio<3, 35>> d3 = d1 - d2;
}

3.时间点 time_point

chrono 库中提供了一个表示时间点的类 time_point,

  • 该类的定义如下:它被实现成如同存储一个 Duration 类型的自 Clock 的纪元起始开始的时间间隔的值,通过这个类最终可以得到时间中的某一个时间点。
// 定义于头文件 <chrono>
template<
    class Clock,
    class Duration = typename Clock::duration
> class time_point;
  • Clock:此时间点在此时钟上计量
  • Duration:用于计量从纪元起时间的 std::chrono::duration 类型
  • time_point 类的构造函数原型如下:
// 1. 构造一个以新纪元(epoch,即:1970.1.1)作为值的对象,需要和时钟类一起使用,不能单独使用该无参构造函数
time_point();
// 2. 构造一个对象,表示一个时间点,其中d的持续时间从epoch开始,需要和时钟类一起使用,不能单独使用该构造函数
explicit time_point( const duration& d );
// 3. 拷贝构造函数,构造与t相同时间点的对象,使用的时候需要指定模板参数
template< class Duration2 >
time_point( const time_point<Clock,Duration2>& t );
  • 在这个类中除了构造函数还提供了另外一个 time_since_epoch() 函数,用来获得 1970 年 1 月 1 日到 time_point 对象中记录的时间经过的时间间隔(duration),函数原型如下:
duration time_since_epoch() const;
  • 除此之外,时间点 time_point 对象和时间段对象 duration 之间还支持直接进行算术运算(即加减运算),时间点对象之间可以进行逻辑运算,具体细节可以参考下面的表格:
    其中 tp 和 tp2 是 time_point 类型的对象, dtn 是 duration 类型的对象。
    在这里插入图片描述
    时间点类经常和下面的时钟类一起使用

4.时钟 clocks

chrono 库中提供了获取当前的系统时间的时钟类,包含的时钟一共有三种:

  • system_clock:系统的时钟,系统的时钟可以修改,甚至可以网络对时,因此使用系统时间计算时间差可能不准。
  • steady_clock:是固定的时钟,相当于秒表。开始计时后,时间只会增长并且不能修改,适合用于记录程序耗时
  • high_resolution_clock:和时钟类 steady_clock 是等价的(是它的别名)。

在这些时钟类的内部有 time_point、duration、Rep、Period 等信息,基于这些信息来获取当前时间,以及实现 time_t 和 time_point 之间的相互转换。
在这里插入图片描述

在使用chrono提供的时钟类的时候,不需要创建类对象,直接调用类的静态方法就可以得到想要的时间了。

system_clock

  • 具体来说,时钟类 system_clock 是一个系统范围的实时时钟。
  • system_clock 提供了对当前时间点 time_point 的访问,将得到时间点转换为 time_t 类型的时间对象,就可以基于这个时间对象获取到当前的时间信息了。
  • system_clock 时钟类在底层源码中的定义如下:
struct system_clock { // wraps GetSystemTimePreciseAsFileTime/GetSystemTimeAsFileTime
    using rep                       = long long;
    using period                    = ratio<1, 10'000'000>; // 100 nanoseconds
    using duration                  = chrono::duration<rep, period>;
    using time_point                = chrono::time_point<system_clock>;
    static constexpr bool is_steady = false;

    _NODISCARD static time_point now() noexcept 
    { // get current time
        return time_point(duration(_Xtime_get_ticks()));
    }

    _NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept 
    { // convert to __time64_t
        return duration_cast<seconds>(_Time.time_since_epoch()).count();
    }

    _NODISCARD static time_point from_time_t(__time64_t _Tm) noexcept 
    { // convert from __time64_t
        return time_point{seconds{_Tm}};
    }
};

通过以上源码可以了解到在 system_clock 类中的一些细节信息:

  • rep:时钟周期次数是通过整形来记录的 long long
  • period:一个时钟周期是 100 纳秒 ratio<1, 10’000’000>
  • duration:时间间隔为 rep*period 纳秒 chrono::duration<rep, period>
  • time_point:时间点通过系统时钟做了初始化 chrono::time_point<system_clock>,里面记录了新纪元时间点

另外还可以看到 system_clock 类一共提供了三个静态成员函数:

// 返回表示当前时间的时间点。
static std::chrono::time_point<std::chrono::system_clock> now() noexcept;
// 将 time_point 时间点类型转换为 std::time_t 类型
static std::time_t to_time_t( const time_point& t ) noexcept;
// 将 std::time_t 类型转换为 time_point 时间点类型
static std::chrono::system_clock::time_point from_time_t( std::time_t t ) noexcept;
  • eg:我们要获取当前的系统时间,并且需要将其以能够识别的方式打印出来,示例代码如下:
#define  _CRT_SECURE_NO_WARNINGS
#include <chrono>
#include <iostream>
using namespace std;
using namespace std::chrono;
int main()
{
    // 新纪元1970.1.1时间
    system_clock::time_point epoch;

    duration<int, ratio<60 * 60 * 24>> day(1);
    // 新纪元1970.1.1时间 + 1天
    system_clock::time_point ppt(day);

    using dday = duration<int, ratio<60 * 60 * 24>>;
    // 新纪元1970.1.1时间 + 10天
    time_point<system_clock, dday> t(dday(10));

    // 系统当前时间
    system_clock::time_point today = system_clock::now();

    // 转换为time_t时间类型
    time_t tm = system_clock::to_time_t(today);
    cout << "今天的日期是:    " << ctime(&tm);

    time_t tm1 = system_clock::to_time_t(today + day);
    cout << "明天的日期是:    " << ctime(&tm1);

    time_t tm2 = system_clock::to_time_t(epoch);
    cout << "新纪元时间:      " << ctime(&tm2);

    time_t tm3 = system_clock::to_time_t(ppt);
    cout << "新纪元时间+1天:  " << ctime(&tm3);

    time_t tm4 = system_clock::to_time_t(t);
    cout << "新纪元时间+10天: " << ctime(&tm4);
}

  • 测试:
    在这里插入图片描述

5.转换函数

未完,,,待续

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
可以使用C++11标准中的`<thread>`头文件来实现多线程,同时使用Kafka提供的Consumer API来消费消息。 在消费者中,可以使用`std::this_thread::sleep_for()`函数来实现时间延迟一分钟。具体实现代码如下: ```c++ #include <iostream> #include <thread> #include <chrono> #include <librdkafka/rdkafkacpp.h> class KafkaConsumer { public: KafkaConsumer(const std::string& brokers, const std::string& topic) : brokers_(brokers), topic_(topic) { // 创建配置对象 RdKafka::Conf* conf = RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL); // 设置Broker列表 std::string errstr; if (conf->set("metadata.broker.list", brokers_, errstr) != RdKafka::Conf::CONF_OK) { std::cerr << "Failed to set broker list: " << errstr << std::endl; exit(1); } // 创建KafkaConsumer对象 consumer_ = RdKafka::Consumer::create(conf, errstr); if (!consumer_) { std::cerr << "Failed to create Kafka consumer: " << errstr << std::endl; exit(1); } // 订阅Topic RdKafka::ErrorCode err = consumer_->subscribe({ topic_ }); if (err != RdKafka::ERR_NO_ERROR) { std::cerr << "Failed to subscribe to topic " << topic_ << ": " << RdKafka::err2str(err) << std::endl; exit(1); } } void consume() { while (true) { RdKafka::Message* msg = consumer_->consume(1000); if (msg->err() == RdKafka::ERR_NO_ERROR) { std::cout << "Received message: " << std::string(static_cast<char*>(msg->payload()), msg->len()) << std::endl; } delete msg; // 延迟一分钟 std::this_thread::sleep_for(std::chrono::minutes(1)); } } private: std::string brokers_; std::string topic_; RdKafka::Consumer* consumer_; }; int main() { KafkaConsumer consumer("localhost:9092", "test_topic"); consumer.consume(); return 0; } ``` 在上面的代码中,`consume()`函数是消费消息的主要逻辑,其中调用了`std::this_thread::sleep_for(std::chrono::minutes(1))`来实现时间延迟一分钟的操作。同时,为了简化代码,省略了一些异常处理和资源释放的代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢打篮球的普通人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值