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\n");
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\n", 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\n");
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\n"); //超时
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\n");
 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\n");
 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中。

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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。