1.timerfd 的基本用法
1.1 timerfd 系列的定时器采用的不是信号,而是 fd 可读,常用的函数有 timerfd_create 、timerfd_settime 和 timerfd_gettime, 这些函数的功能和用法也比较浅显,这里用一个简单的例子(1.2)来说明其用法,create 参数中 CLOCK_REALTIME 是一个相对时间,当系统时间被更改时会进行调整,还有一个参数CLOCK_MONOTONIC,它是绝对时间,更改系统时间对它没有影响,这里flags一般设为0,前面说到,定时器到期表现为 timerfd 可读,因此就可以把该fd加入到IO复用模型当中去(1.3)。
1.2 创建一个定时器,设置好时间间隔结构体的参数,开启定时器,这样一个定时器就可以工作了,但是这里要注意,timerfd 定时器到期时表现为该 fd 有数据可读,因此一旦到时,一定要把数据 read 出去,不然定时器就无法正常工作。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/timerfd.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) /* * 定时器timerfd的基础用法 */ int main(int argc, const char *argv[]) { //创建定时器的fd int timerfd = timerfd_create(CLOCK_REALTIME, 0); if(timerfd == -1) ERR_EXIT("timerfd_create"); //开启定时器,并设置定时器的时间 struct itimerspec new_value; // const memset(&new_value, 0, sizeof new_value); new_value.it_value.tv_sec = 5; //初始到期时间 new_value.it_interval.tv_sec = 1; //之后的间隔时间 if(timerfd_settime(timerfd, 0, &new_value, NULL) == -1) ERR_EXIT("timerfd_settime"); char buf[1024] = {0}; int ret; while((ret = read(timerfd, buf, sizeof buf)) > 0){ printf("ret = %d, read data:%s\n", ret, buf); // } close(timerfd); return 0; }
1.3 将timer和poll一起使用,timerfd 加入到 poll 的监听数组中,当 timer 到期,poll 就会监听到该 timerfd。这里 poll 采用的是水平触发模式,也就是说对于某 fd 可读,如果不做 read 处理,那么下次会再次返回该 fd,即该 fd一直可读,等待着被读出去,如果不读出去,就一直被触发,那么定时器就没用了。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <poll.h> #include <sys/timerfd.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) /* * 定时器timerfd 和 epoll 一起使用 */ int main(int argc, const char *argv[]) { //创建定时器的fd int timerfd = timerfd_create(CLOCK_REALTIME, 0); if(timerfd == -1) ERR_EXIT("timerfd_create"); //开启定时器,并设置定时器的时间 struct itimerspec new_value; // const memset(&new_value, 0, sizeof new_value); new_value.it_value.tv_sec = 5; //初始到期时间 new_value.it_interval.tv_sec = 1; //之后的间隔时间 if(timerfd_settime(timerfd, 0, &new_value, NULL) == -1) ERR_EXIT("timerfd_settime"); char buf[1024] = {0}; struct pollfd event[1]; event[0].fd = timerfd; event[0].events = POLLIN; while(1){ int ret = poll(event, 1, 10000);//等待的最长时间是10s if(ret == -1) ERR_EXIT("poll"); else if(ret == 0) printf("timeout\n"); else{ if(read(timerfd, buf, sizeof buf) == -1) //这里如果不read 会一直被触发 ERR_EXIT("read"); printf("foobar....\n"); } } }
1.4 一个Timer类,这里把 timerfd 类的函数封装成类,由用户提供定时器到期时要执行的操作(即使用函数回调),这样做的好处,也就是面向对象的好处,对外界提供接口函数,需要时直接用对象调用,更加安全。
#ifndef __TIMER_H__ #define __TIMER_H__ #include "NonCopyable.h" #include <functional> #include <sys/timerfd.h> class Timer : NonCopyable{ public: typedef std::function<void ()> TimerCallback; //定时器回调函数类型 Timer(); ~Timer(); void setTimer(int init_val, int inter_val); //参数为初始到期时间 和 以后每次的间隔时间 void setCallback(const TimerCallback &callback); void runTimer(); private: int timerfd_; struct itimerspec howlong_; TimerCallback callback_; //用户自定义的逻辑 }; #endif /*__TIMER_H__*/ #include "Timer.h" #include <string.h> #include <stdlib.h> #include <stdio.h> #include <poll.h> #include <iostream> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) Timer::Timer(){ timerfd_ = timerfd_create(CLOCK_REALTIME, 0); if(timerfd_ == -1) ERR_EXIT("timer_create"); memset(&howlong_, 0, sizeof howlong_); } Timer::~Timer(){ close(timerfd_); } void Timer:: setTimer(int init_val, int inter_val){ //参数 howlong_.it_value.tv_sec = init_val; howlong_.it_interval.tv_sec = inter_val; } void Timer::setCallback(const TimerCallback &callback){ callback_ = callback; } void Timer::runTimer(){ //开启定时器 if(timerfd_settime(timerfd_, 0, &howlong_, NULL) == -1) ERR_EXIT("timerfd_settime"); struct pollfd event[1]; event[0].fd = timerfd_; event[0].events = POLLIN; char buf[1024]; int ret; while(1){ ret = poll(event, 1, 1000); //每隔1s 轮询一次 if(ret == -1) ERR_EXIT("poll"); else if(ret == 0) std::cout << "timerout" << std::endl; else{ if(read(timerfd_, buf, sizeof buf)== -1) ERR_EXIT("read"); callback_(); //调用用户逻辑 } } } #include "Timer.h" #include <iostream> using namespace std; void func(){ cout << "hello world" << endl; } int main(int argc, const char *argv[]) { Timer tm; tm.setTimer(3, 1); tm.setCallback(func); tm.runTimer(); return 0; }
2.Timer 和 Thread 类的组合
2.1 程序编写思路:
a)把用户逻辑 bind 到 Timer 里面;
b)把Timer 的 runTimer 方法bind 到 Thread 里面;
c)也就是说,把 Timer 的操作封装到线程里,执行线程也就是开启定时器。
2.2 源程序清单
a)Thread.h Thread.cpp
#ifndef __THREAD_H__ #define __THREAD_H__ #include "NonCopyable.h" #include <pthread.h> #include <functional> class Thread : NonCopyable{ public: typedef std::function<void ()> ThreadCallback; Thread(); explicit Thread(const ThreadCallback &callback); ~Thread(); void setCallback(const ThreadCallback &callback); void start(); void join(); private: static void *thread_func(void *); pthread_t tid_; ThreadCallback callback_; bool isStarted_; }; #endif /*__THREAD_H__*/ #include "Thread.h" Thread::Thread() :tid_(-1), isStarted_(false) { } Thread::Thread(const ThreadCallback &callback) :tid_(-1), isStarted_(false), callback_(callback) { } Thread::~Thread(){ if(isStarted_) pthread_detach(tid_); } void Thread::setCallback(const ThreadCallback &callback){ callback_ = callback; } void Thread::start(){ isStarted_ = true; pthread_create(&tid_, NULL, thread_func, this); } void Thread::join(){ isStarted_ = false; pthread_join(tid_, NULL); } void *Thread::thread_func(void *arg){ Thread *pt = static_cast<Thread *>(arg); pt->callback_(); return NULL; }
b) test_timer.cpp
#include "Timer.h" #include "Thread.h" #include <iostream> using namespace std; /* * 使用类的组合 Thread 和 Timer 类 */ class TimerThread{ public: TimerThread(); void print(); //定时器到期时 执行的函数 void startTimerThread(); private: Thread thread_; Timer timer_; }; TimerThread::TimerThread(){} void TimerThread::print(){ cout << "hello world" << endl; } void TimerThread::startTimerThread(){ timer_.setTimer(3, 1); timer_.setCallback(bind(&TimerThread::print, this)); // 线程类用来封装Timer的操作 thread_.setCallback(bind(&Timer::runTimer, &timer_)); thread_.start(); thread_.join(); } int main(int argc, const char *argv[]) { TimerThread tt; tt.startTimerThread(); return 0; }
0804-----Linux基础----------定时器之 timerfd