zoukankan      html  css  js  c++  java
  • Linux组件封装(六)——定时器的简单封装

    在Linux中,有一种简单的定时器——timerfd,它通过查看fd是否可读来判断定时器时候到时。

    timerfd中常用的函数有timerfd_create、timerfd_settime、timerfd_gettime,这些函数都相对简单,我们可以到man手册来查看用法。

    值得注意的是:create中的参数CLOCK_REALTIME是一个相对时间,我们可以通过调整系统时间对其进行调整,而CLOCK_MONOTIC是一个绝对时间,系统时间的改变不会影响它。在create中,flags一般设置为0。

    下面是一个简单的例子:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <unistd.h>
     5 #include <sys/types.h>
     6 #include <errno.h>
     7 #include <sys/timerfd.h>
     8 #include <stdint.h>
     9 #include <poll.h>
    10 #define ERR_EXIT(m) 
    11     do { 
    12         perror(m);
    13         exit(EXIT_FAILURE);
    14     }while(0)
    15 
    16 void foo()
    17 {
    18     printf("foo
    ");
    19 }
    20 
    21 
    22 int main(int argc, const char *argv[])
    23 {
    24     //创建fd
    25 
    26     int timerfd = timerfd_create(CLOCK_REALTIME, 0);
    27     if(timerfd == -1)
    28         ERR_EXIT("timerfd_create");
    29 
    30     //设置时间
    31     struct itimerspec tv;
    32     memset(&tv, 0, sizeof tv);
    33     tv.it_value.tv_sec = 3;
    34     tv.it_interval.tv_sec = 1;
    35     if(timerfd_settime(timerfd, 0, &tv, NULL) == -1)
    36         ERR_EXIT("timerfd_settime");
    37 
    38     char buf[1024] = {0}; 
    39     int ret; 
    40     while((ret = read(timerfd, buf, sizeof buf)) > 0){ 
    41         printf("ret = %d, read data:%s
    ", ret, buf); // 
    42     } 
    43 
    44     close(timerfd);
    45 
    46     return 0;
    47 }
    View Code

    这里需要注意:一旦定时器到期,fd中就有数据可读,这个时候,我们一定要将fd中的数据read出来,否则定时器会产生异常,无法正常工作。

    我们可以将timerfd和poll一起使用,将timerfd加入到poll的监听数组中,这样当timerfd可读时,我们就调用相应的函数,来完成定时任务。

    然而当我们不将timerfd中的数据读出时,poll监听到timerfd一直可读,这样就会一直触发相应函数,就失去了定时器的左右。

    所以,我们一定要注意将timerfd中的数据读出

    将timerfd与poll结合:

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #include <string.h>
     4 #include <unistd.h>
     5 #include <sys/types.h>
     6 #include <errno.h>
     7 #include <sys/timerfd.h>
     8 #include <stdint.h>
     9 #include <poll.h>
    10 #define ERR_EXIT(m) 
    11     do { 
    12         perror(m);
    13         exit(EXIT_FAILURE);
    14     }while(0)
    15 
    16 void foo()
    17 {
    18     printf("foo
    ");
    19 }
    20 
    21 
    22 int main(int argc, const char *argv[])
    23 {
    24     //创建fd
    25 
    26     int timerfd = timerfd_create(CLOCK_REALTIME, 0);
    27     if(timerfd == -1)
    28         ERR_EXIT("timerfd_create");
    29 
    30     //设置时间
    31     struct itimerspec tv;
    32     memset(&tv, 0, sizeof tv);
    33     tv.it_value.tv_sec = 3;
    34     tv.it_interval.tv_sec = 1;
    35     if(timerfd_settime(timerfd, 0, &tv, NULL) == -1)
    36         ERR_EXIT("timerfd_settime");
    37 
    38     //判断fd可读
    39 
    40     //int poll(struct pollfd *fds, nfds_t nfds, int timeout);
    41 
    42     struct pollfd pfd;
    43     pfd.fd = timerfd;
    44     pfd.events = POLLIN; //监听输入事件
    45 
    46     uint64_t val;
    47     int ret;
    48     while(1)
    49     {
    50         ret = poll(&pfd, 1, 5000);
    51         if(ret == -1)
    52         {
    53             if(errno == EINTR)
    54                 continue;
    55             ERR_EXIT("poll");
    56         }
    57         else if(ret == 0)
    58         {
    59             printf("timeout
    "); //超时
    60         }
    61 
    62         if(pfd.revents == POLLIN) //此fd是否监听的read事件
    63         {
    64             read(timerfd, &val, sizeof val);
    65             foo();
    66         }
    67             
    68     }
    69 
    70 
    71     close(timerfd);
    72 
    73     return 0;
    74 }
    View Code

    我们可以将timerfd类的函数封装到一个Timer类中,提供一个接口来接受用户要执行的函数(即回调函数),这样做,可以使我们的定时器更加安全、实用。
    声明代码如下:

     1 #ifndef TIMER_H
     2 #define TIMER_H
     3 #include <boost/noncopyable.hpp>
     4 #include <functional>
     5 #include <sys/timerfd.h>
     6 
     7 class Timer : boost::noncopyable
     8 {
     9 public:
    10 
    11     typedef std::function<void()> TimerCallback;
    12 
    13     Timer(int val, int interval, TimerCallback cb);
    14     ~Timer();
    15 
    16     void start();
    17     void stop();
    18 
    19 private:
    20 
    21     int _timerfd;
    22     int _val;
    23     int _interval;
    24     TimerCallback _callback;
    25     bool _isStart;
    26 };
    27 
    28 
    29 
    30 #endif  /*TIMER_H*/
    View Code

    实现代码如下:

      1 #include "Timer.h"
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <string.h>
      5 #include <unistd.h>
      6 #include <sys/types.h>
      7 #include <errno.h>
      8 #include <sys/timerfd.h>
      9 #include <stdint.h>
     10 #include <poll.h>
     11 #define ERR_EXIT(m) 
     12         do { 
     13             perror(m);
     14             exit(EXIT_FAILURE);
     15         }while(0)
     16 namespace
     17 {
     18     int createTimer()
     19     {
     20         int timerfd = ::timerfd_create(CLOCK_REALTIME, 0);
     21         if(timerfd == -1)
     22             ERR_EXIT("create");
     23 
     24         return timerfd;
     25     }
     26 
     27     void setTimer(int timerfd, int val, int interval)
     28     {
     29         struct itimerspec t;
     30         memset(&t, 0, sizeof t);
     31         t.it_value.tv_sec = val;
     32         t.it_interval.tv_sec = interval;
     33 
     34         if(::timerfd_settime(timerfd, 0, &t, NULL) == -1)
     35             ERR_EXIT("settime");
     36     }
     37 
     38     void stopTimer(int timerfd)
     39     {
     40         setTimer(timerfd, 0, 0);
     41     }
     42 
     43     void readTimer(int timerfd)
     44     {
     45         uint64_t howmany;
     46         if(::read(timerfd, &howmany, sizeof howmany) != sizeof(howmany))
     47             ERR_EXIT("read");
     48     }
     49 
     50 }
     51 Timer::Timer(int val, int interval, TimerCallback cb)
     52     :_timerfd(createTimer()),
     53      _val(val),
     54      _interval(interval),
     55      _callback(std::move(cb)),
     56      _isStart(false)
     57 {
     58 
     59 }
     60 
     61 
     62 Timer::~Timer()
     63 {
     64     if(_isStart)
     65     {
     66         stop();
     67         ::close(_timerfd);
     68     }
     69 }
     70 
     71 
     72 void Timer::start()
     73 {
     74     setTimer(_timerfd, _val, _interval);
     75     _isStart = true;
     76 
     77     struct pollfd pfd;
     78     pfd.fd = _timerfd;
     79     pfd.events = POLLIN;
     80 
     81     uint64_t val;
     82     int ret;
     83     while(_isStart)
     84     {
     85         ret = ::poll(&pfd, 1, 5000);
     86         if(ret == -1)
     87         {
     88             if(errno == EINTR)
     89                 continue;
     90             ERR_EXIT("poll");
     91         }
     92         else if(ret == 0)
     93         {
     94             printf("timeout
    ");
     95             continue;
     96         }
     97 
     98         if(pfd.revents == POLLIN)
     99         {
    100             readTimer(_timerfd);
    101             _callback();
    102         }
    103     }
    104 }
    105 
    106 void Timer::stop()
    107 {
    108     _isStart = false;
    109     stopTimer(_timerfd);
    110 }
    View Code

    我们可以将定时器与线程封装到一起,这样,每个线程就是一个定时器。

    线程的封装如下:

     1 #ifndef THREAD_H
     2 #define THREAD_H
     3 #include <boost/noncopyable.hpp>
     4 #include <functional>
     5 #include <pthread.h>
     6 class Thread : boost::noncopyable
     7 {
     8 public:
     9 
    10     typedef std::function<void()> ThreadCallback;
    11 
    12     Thread(ThreadCallback cb);
    13     ~Thread();
    14 
    15     void start();
    16     void join();
    17 
    18     static void *runInThread(void *);
    19 
    20 
    21 private:
    22 
    23     pthread_t _threadId;
    24     bool _isRun;
    25     ThreadCallback _callback;
    26 };
    27 
    28 
    29 #endif  /*THREAD_H*/
    View Code
     1 #include "Thread.h"
     2 
     3 Thread::Thread(ThreadCallback cb)
     4     :_threadId(0),
     5      _isRun(false),
     6      _callback(cb)
     7 {
     8 
     9 }
    10 
    11 Thread::~Thread()
    12 {
    13     if(_isRun)
    14         pthread_detach(_threadId);
    15 }
    16 
    17 
    18 void Thread::start()
    19 {
    20     pthread_create(&_threadId, NULL, runInThread, this);
    21     _isRun = true;
    22 }
    23 
    24 void Thread::join()
    25 {
    26     pthread_join(_threadId, NULL);
    27     _isRun = false;
    28 }
    29 
    30 void *Thread::runInThread(void *arg)
    31 {
    32     Thread *p = static_cast<Thread *>(arg);
    33     p->_callback();
    34     return NULL;
    35 }
    View Code

    TimerThread的封装如下:

     1 #ifndef TIMER_THREAD_H
     2 #define TIMER_THREAD_H
     3 #include <boost/noncopyable.hpp>
     4 #include <functional>
     5 #include "Timer.h"
     6 #include "Thread.h"
     7 
     8 class TimerThread : boost::noncopyable
     9 {
    10 public:
    11     typedef std::function<void()> Callback;
    12     TimerThread(int val, int interval, Callback cb);
    13 
    14     void start();
    15     void stop();
    16 
    17 private:
    18     Timer _timer;
    19     Thread _thread;
    20 
    21 };
    22 
    23 
    24 #endif  /*TIMER_THREAD_H*/
    View Code
     1 #include "TimerThread.h"
     2 
     3 TimerThread::TimerThread(int val, int interval, Callback cb)
     4     :_timer(val, interval, std::move(cb)),
     5      _thread(std::bind(&Timer::start, &_timer))
     6 {
     7 
     8 }
     9 
    10 void TimerThread::start()
    11 {
    12     _thread.start();
    13 }
    14 
    15 void TimerThread::stop()
    16 {
    17     _timer.stop();
    18     _thread.join();
    19 }
    View Code

    测试函数如下:

     1 #include "TimerThread.h"
     2 #include <stdio.h>
     3 #include <unistd.h>
     4 
     5 void foo()
     6 {
     7     printf("foo
    ");
     8 }
     9 
    10 int main(int argc, const char *argv[])
    11 {
    12     TimerThread a(3, 1, &foo);
    13     a.start();
    14     sleep(10);
    15     a.stop();
    16     return 0;
    17 }
    View Code

    在将Timer与Thread结合封装中,我们需要注意:
      a)首先,将用户要执行的任务函数bind到Timer中。

      b)然后,我们将Timer的start函数bind到Thread中。

    这样,当我们开启一个该类的线程就相当于开启了一个定时器。

  • 相关阅读:
    L317 电子烟
    L316 波音737Max 危机
    2.19.3月 专业综合错题
    L314 单音节词读音规则(二)-元音字母发音规则
    L313 珊瑚裸鼠灭绝
    L312 难看懂的
    Pycharm写代码中文输入法不跟随
    Windows下Python多版本共存
    Python之批处理字符串(打开文件)
    Python Url请求代码
  • 原文地址:https://www.cnblogs.com/gjn135120/p/4023129.html
Copyright © 2011-2022 走看看