首页 > 代码库 > Linux组件封装(六)——定时器的简单封装
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 //创建fd25 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 }
这里需要注意:一旦定时器到期,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 //创建fd25 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 }
我们可以将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*/
实现代码如下:
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 }
我们可以将定时器与线程封装到一起,这样,每个线程就是一个定时器。
线程的封装如下:
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*/
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 }
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*/
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 }
测试函数如下:
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 }
在将Timer与Thread结合封装中,我们需要注意:
a)首先,将用户要执行的任务函数bind到Timer中。
b)然后,我们将Timer的start函数bind到Thread中。
这样,当我们开启一个该类的线程就相当于开启了一个定时器。
Linux组件封装(六)——定时器的简单封装