跟我学c++中级篇—c++11时间库实现定时器和延时

一、C++11时间库

先简单介绍一下C++11中有三类时钟:
1、system_clock:可以理解为壁钟,可调整(向前或向后),是系统时间。它可以 与C语言风格的时间进行映射。
2、steady_clock:类似于秒表的单调时钟,只可增长不能修改,最适合进行时间间隔的测量。
3、high_resolution_clock:高精度的时钟(最小计次周期),不可以修改的,可以将其看做 steady_clock或system_clock 的高精度版的别名(gcc 的 libstdc++ 它是 system_clock ,对于 MSVC 它是 steady_clock ,而对于 clang 的 libc++ 它取决于配置)。对时长度量使用 steady_clock ,对壁钟时间使用 system_clock
此处不对C++11中的时钟进行详细分析,只关心几种应用方式,下面逐一说明分析。

二、应用的介绍

这里面有一疑问的可以看一下下面的程序,对sleep_for和sleep_until的使用是延时和定时器的主要方式,它们都是延时,一个到点,一个停止一段。有的资料说sleep_for会导致整个进程的所有线程阻塞,但在标准文档写的是当前线程阻塞,下面的代码就可以验证是哪种。

#include <chrono>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <thread>
#include <unistd.h>

using namespace std;

void for_thread() {
  this_thread::sleep_for(chrono::seconds(6));
  cout << "sleep_for:" << this_thread::get_id() << " is  start" << endl;
}

void loop_thread() {
  //sleep(1);
  for (int i = 0; i < 10; i++) {
    cout << "loop thread:" << this_thread::get_id() << "cur value: " << i << endl;
  }
}
void until_thread() {
  using std::chrono::operator""s;
  chrono::time_point t = std::chrono::steady_clock::now() + 3s;
  this_thread::sleep_until(t);
  cout << "sleep_until:" << this_thread::get_id() << "is  start" << endl;
}

int main() {
  thread t1(for_thread);
  thread t2(loop_thread);
  thread t3(until_thread);

  t2.join();
  t1.join();
  t3.join();

  return 0;
}

很多情况都需要实践操作一下,即使是操作的有问题,也可以 更好的理解问题本身和应用库的特点。

三、定时器应用

1、做定时器

void timerLoop() {
  bool Continue = true;
  while (Continue) {
    Continue = timerCallback();//回调函数,也就是定时器定时触发的函数
    if (Continue) {
      m_timer.sleepUntil();//时间间隔
    }
  }
}
//间隔设置用下而把 方法
double freq = 30.0;//此值可动态调整
duration = duration_cast<std::chrono::duration<long, std::micro>>(microseconds((long long)round(1e6 / freq)));
//可以理解为时间轮的槽的长度
void sleepUntil() {

  if (Done) {
     clock::time_point nextSlot = begin + duration;

     // sleep
    std::this_thread::sleep_until(nextSlot);
  } else {
    Done = true;
  }
  begin = std::chrono::high_resolution_clock::now();
}

最初在学习定时器时,就不断看到说定时器其实就是一个独立的线程,现在自己实现一把,就更明白底层的实现了。原理基本都是类似的,重点在于两个,一个是写得要安全;另外一个是适应性要好。

2、延时(阻塞调用)
如果用在一种和需要阻塞一段时间才能拿到结果的API调用时,这种方法就有用了,以前好多人都是用死循环(硬件嵌入式比较多),但这种可能会使整个进程被占用。但在上层一般会用回调或者事件等方式来处理。不过总有一些情况需要延时,所以就可以 用这种来实现。如上面的函数:

void until_other() {
  std::chrono::time_point<std::chrono::steady_clock> now;
  now = std::chrono::steady_clock::now();
  std::this_thread::sleep_until(now + std::chrono::milliseconds(1000)+ std::chrono::microseconds(100));
  cout << "sleep_until_other:" << this_thread::get_id() << "is  start" << endl;
}

sleep_for比较简单,就不举例了。

3、计时
先看一下传统的测量耗时的方法

  struct timeval t1, t2;
  double timeuse;
  gettimeofday(&t1, NULL);
  //此处调用函数,用来测试时间
  gettimeofday(&t2, NULL);
  timeuse = t2.tv_sec - t1.tv_sec + (t2.tv_usec - t1.tv_usec) / 1000000.0;
  printf("readmeme Use Time:%f\n", timeuse);

再看一个用C++11时间库的:

#include <iostream>
#include <vector>
#include <numeric>
#include <chrono>

volatile int sink;
int main()
{
    for (auto size = 1ull; size < 1000000000ull; size *= 100) {
        // 记录开始时间
        auto start = std::chrono::high_resolution_clock::now();
        // 做一些工作
        std::vector<int> v(size, 42);
        sink = std::accumulate(v.begin(), v.end(), 0u); // 确保其副效应
        // 记录结束时间
        auto end = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> diff = end-start;
        std::cout << "Time to fill and iterate a vector of "
                  << size << " ints : " << diff.count() << " s\n";
    }
}

还有一些细小的应用大家可以 在实际用到查一下文档即可。

四、总结

在C++11中利用时间库加线程就可以比较轻松的实现一个跨平台的定时器。这比在Linux的其它定时器的实现既简单又方便,精度也可以自己确定,这在一般的通用场合就足够了。简单就是王道,这个是无法比拟的。
最近总是有些想法想说一说。现在是知识爆炸的年代,许多技术知识在网上是可以很容易搜到的。但这些知识,有大牛给出的,有小牛给的,有经验丰富的开发者给的,更多的是一些应用者,甚至还有一些是初学者,把一些掌握的问题解法和知识心得发表到网上。这些技术或者解决技术难题的方法,从整体上看,相当多都只是某一方面上的,也可以说是很片面的。对不同的人群来说,可能这些知识所给予的心得各有不同。但从不同的角度来看,可能一些受众是初学者的,大牛们的文章反而短时间内不好消化吸收。学习终归是从点突破到线发展,最后到面扩展,到最终生成一个立体的知识球体也就是个人的认知体系,也是网上说的知识蚕茧。
有的人学习可能会是不断的打破这个茧,更多的人反而是强化这个茧。这就是牛人和非牛人的区别。说这么多是什么 意思呢?网上的知识被个体搜索到后,要有分辨能力,哪些能为我所用,哪些不能,哪些是正确的,哪些是错误的,哪些是片面的。网上的知识,很多是提供思路和灵感,不是说拷贝过来就用。要善于进行拿来主义,而不是僵化的拿来主义。要有哲学上否定之否定的思想,不断的总结和升华自己的知识体系,打破固有的知识牢笼或者说知识蚕茧。
学习永远在路上,变化才是永恒!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 在Linux C中实现定时器,我们可以使用信号处理机制来实现。具体步骤如下: 1. 引入头文件:包括<sys/time.h> 和 <signal.h>。 2. 定义信号处理函数:在信号处理函数中定义定时器到期时的操作。例如,可以在信号处理函数中输出一条定时器到期的消息。 3. 设置定时器:使用setitimer函数来设置定时器的间隔时间和初始启动时间,并指定信号处理函数。setitimer函数需要传入一个结构体itimerval作为参数,该结构体包含两个成员:it_value代表第一次到期的时间,it_interval代表后续到期的时间间隔。 4. 阻塞信号:使用sigaction函数阻塞相关信号,以免在处理定时器到期时被其他信号打断。 5. 开启定时器:使用信号处理函数来触发定时器,并在定时器到期时执行相关操作。 以下是一个简单的示例代码: ```C #include <stdio.h> #include <sys/time.h> #include <signal.h> void timer_handler(int signum) { printf("Timer expired!\n"); } int main() { struct itimerval timer; // 设置定时器间隔为2秒,并初始化定时器 timer.it_value.tv_sec = 2; timer.it_value.tv_usec = 0; timer.it_interval.tv_sec = 2; timer.it_interval.tv_usec = 0; // 设置信号处理函数 signal(SIGALRM, timer_handler); // 设置定时器 setitimer(ITIMER_REAL, &timer, NULL); // 阻塞相关信号 sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGALRM); sigprocmask(SIG_BLOCK, &mask, NULL); // 循环等待定时器到期 while(1) { pause(); } return 0; } ``` 以上代码中,我们通过设置定时器的间隔时间和初始启动时间来控制定时器的重复触发。在信号处理函数中,我们通过输出一条消息来表示定时器到期的事件。在主函数中,我们首先设置信号处理函数,然后设置定时器并开启定时器,并最后通过循环等待定时器到期来保持程序的运行。 ### 回答2: 在Linux C编程中,实现定时器可以使用信号机制来达到目的。下面是一个简单的例子,展示了如何实现一个定时器。 首先,需要包含头文件`<unistd.h>`和`<signal.h>`,以便使用相关的函数和宏定义。 然后,定义一个用于处理定时器的信号处理函数,例如命名为`timer_handler`。在这个函数中,可以执行特定的操作作为定时器触发后的处理逻辑。在下面的例子中,我们只是简单地打印一条消息。 接下来,创建一个`timer_t`类型的变量,用于存储定时器的ID。可以使用`timer_create`函数创建一个新的定时器,并传入相关的参数,如定时器类型、信号处理函数等。 然后,使用`timer_settime`函数设置定时器时间参数,包括初始时间和间隔时间。在下面的例子中,我们将定时器设置为3秒后启动,并且每隔5秒触发一次。 最后,使用`sleep`函数使程序暂停,以便触发定时器。在实际应用中,可以根据需要将这个定时器与其他功能集成。 下面是一个完整的例子代码: ```c #include <unistd.h> #include <signal.h> #include <stdio.h> void timer_handler(int signum) { printf("Timer expired.\n"); } int main() { // 创建一个新的定时器 timer_t timerid; struct sigevent sev; struct itimerspec its; sev.sigev_notify = SIGEV_SIGNAL; sev.sigev_signo = SIGALRM; sev.sigev_value.sival_ptr = &timerid; timer_create(CLOCK_REALTIME, &sev, &timerid); // 设置定时器参数 its.it_value.tv_sec = 3; its.it_value.tv_nsec = 0; its.it_interval.tv_sec = 5; its.it_interval.tv_nsec = 0; timer_settime(timerid, 0, &its, NULL); // 暂停程序,等待定时器触发 sleep(20); return 0; } ``` 在上述的例子中,我们创建了一个3秒后启动的定时器,并且每隔5秒触发一次。程序将在主函数中的`sleep`函数处暂停20秒,期间定时器会触发三次,并打印"Timer expired."的消息。 ### 回答3: 在Linux C中,我们可以使用`timer_create()`函数来创建一个定时器。该函数接受四个参数:一个时钟ID,一个用于保存定时器 ID 的变量,一个结构体指针以指定定时器的属性,以及一个可选的回调函数。 要创建一个定时器,首先需要定义一个 `timer_t` 类型的变量来保存定时器 ID。然后,要使用 `timer_create()` 函数创建定时器,并将定时器 ID 保存到该变量中。 接下来,需要定义一个结构体变量来指定定时器的属性。可以使用 `struct sigevent` 结构体,并根据需要设置其成员变量。例如,我们可以将 `sigev_notify` 成员设置为 `SIGEV_THREAD`,以指定定时器到期时,将调用一个线程执行回调函数。 在回调函数中,可以执行想要执行的操作。可以在回调函数中做一些计算、输出等,或者执行某个函数或方法。 接下来,我们需要使用 `timer_settime()` 函数来启动定时器,并设置执行回调函数的时间间隔。此函数接受四个参数:定时器 ID、指定定时器何时到期的结构体指针、一个用于保存之前定时器设置的结构体指针,并通过第四个参数来指定相对于哪个时间来设置定时器。 综上所述,实现定时器的步骤如下: 1. 定义 `timer_t` 类型的变量来保存定时器 ID。 2. 使用 `timer_create()` 函数创建定时器。 3. 定义一个结构体变量来指定定时器的属性。 4. 在回调函数中定义要执行的操作。 5. 使用 `timer_settime()` 函数启动定时器,并设置执行回调函数的时间间隔。 需要注意的是,创建定时器的函数及相关数据结构在`<time.h>`头文件中声明。在使用定时器时,可能还需要使用信号和线程相关的函数和数据结构。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值