C工具9:定时器
程序项目经常用到定时功能,如网络程序中,每隔固定的时间将发送缓冲中的数据一次性发往对端.
下面介绍一个用posix timerfd实现的定时器, timerfd将定时器当做一个文件描述符,当定时器
到时fd变为可读,可以将这个描述符交给epoll监听,timeout的时候由epoll返回并执行回调.
timer.h
#ifndef _TIMER_H #define _TIMER_H #define MAX_TIMER 4096 typedef struct Timer *Timer_t; typedef void (*timer_callback)(Timer_t,void*); typedef struct TimerMgr *TimerMgr_t; extern TimerMgr_t CreateTimerMgr(); extern void DestroyTimerMgr(TimerMgr_t*); //如果once=1则调用后RunTimerMgr马上返回,否则之后等TerminateTimerMgr调用之后才会返回 extern void RunTimerMgr(TimerMgr_t,int once); extern void TerminateTimerMgr(TimerMgr_t); extern int AddTimer(TimerMgr_t,Timer_t); extern int RemoveTimer(TimerMgr_t,Timer_t); extern Timer_t CreateTimer(struct itimerspec*,timer_callback,void *arg); extern void DestroyTimer(Timer_t*); //默认的itimerspec结构初始器,用于创建固定间隔定时器 extern void DefaultInit(struct itimerspec*,long interval); #ifndef TIMERMGR_RUNONCE #define TIMERMGR_RUNONCE(TIMERMGR) RunTimerMgr(TIMERGER,1) #endif #ifndef TIMERMGR_RUN #define TIMERMGR_RUN(TIMERMGR) RunTimerMgr(TIMERGER,0) #endif //创建一个默认的固定间隔定时器,最小单位毫秒 #ifndef DEFAULT_TIMER #define DEFAULT_TIMER(INTERVAL,CALLBACK,ARG)\ ({Timer_t __ret;struct itimerspec __spec;\ DefaultInit(&__spec,INTERVAL);\ __ret = CreateTimer(&__spec,CALLBACK,ARG);\ __ret;}) #endif #endif
TimerMgr_t用于管理所有的定时器,RunTimerMgr用于启动epoll主循环,另一种实现方式是CreateTimerMgr
后在后台自动创建一个线程运行epoll主循环,但我倾向于只提供机制,让用户按自己的需求使用接口.
timer.c
#include <stdio.h> #include <stdlib.h> #include <sys/timerfd.h> #include "SocketWrapper.h" #include "timer.h" #include "epoll.h" struct TimerMgr { int epollfd; volatile int terminated; struct epoll_event events[MAX_TIMER]; }; struct Timer { int fd; void *arg;//callback的第二个参数 timer_callback callback; }; TimerMgr_t CreateTimerMgr() { int epollfd = TEMP_FAILURE_RETRY(epoll_create(MAX_TIMER)); if(epollfd>=0) { TimerMgr_t t = malloc(sizeof(*t)); t->epollfd = epollfd; memset(t->events,0,sizeof(t->events)); return t; } return 0; } void DestroyTimerMgr(TimerMgr_t *t) { close((*t)->epollfd); free(*t); *t = 0; } void TerminateTimerMgr(TimerMgr_t t) { t->terminated = 1; } void RunTimerMgr(TimerMgr_t t,int once) { t->terminated = 0; long long tmp; while(!t->terminated && !once) { int nfds = TEMP_FAILURE_RETRY(epoll_wait(t->epollfd,t->events,MAX_TIMER,100)); if(nfds < 0) { t->terminated = 1; break; } int i; for(i = 0 ; i < nfds ; ++i) { Timer_t _timer = (Timer_t)t->events[i].data.ptr; read(_timer->fd,&tmp,sizeof(tmp)); if(_timer->callback) _timer->callback(_timer,_timer->arg); } } t->terminated = 1; } int AddTimer(TimerMgr_t t,Timer_t _timer) { int ret; struct epoll_event ev; ev.data.ptr = _timer; ev.events = EV_IN | EV_OUT; TEMP_FAILURE_RETRY(ret = epoll_ctl(t->epollfd,EPOLL_CTL_ADD,_timer->fd,&ev)); if(ret != 0) return -1; return 0; } int RemoveTimer(TimerMgr_t t,Timer_t _timer) { int ret; struct epoll_event ev; TEMP_FAILURE_RETRY(ret = epoll_ctl(t->epollfd,EPOLL_CTL_DEL,_timer->fd,&ev)); if(ret != 0) return -1; return 0; } void DefaultInit(struct itimerspec *new_value,long interval) { struct timespec now; clock_gettime(/*CLOCK_REALTIME*/CLOCK_MONOTONIC, &now); int sec = interval/1000; int ms = interval%1000; long long nosec = (now.tv_sec + sec)*1000*1000*1000 + now.tv_nsec + ms*1000*1000; new_value->it_value.tv_sec = nosec/(1000*1000*1000); new_value->it_value.tv_nsec = nosec%(1000*1000*1000); new_value->it_interval.tv_sec = sec; new_value->it_interval.tv_nsec = ms*1000*1000; } Timer_t CreateTimer(struct itimerspec *spec,timer_callback callback,void *arg) { int fd = timerfd_create(/*CLOCK_REALTIME*/CLOCK_MONOTONIC,0); if(fd < 0) return 0; Timer_t t = malloc(sizeof(*t)); if(!t) { close(fd); return 0; } t->callback = callback; t->fd = fd; t->arg = arg; timerfd_settime(fd,TFD_TIMER_ABSTIME,spec,0); return t; } void DestroyTimer(Timer_t *t) { free(*t); *t = 0; }
test.c
#include <stdio.h> #include <stdlib.h> #include <sys/timerfd.h> #include "timer.h" static int total = 0; void test_callback(Timer_t t,void *arg) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); printf("%d,%ld\n",now.tv_sec,now.tv_nsec); ++total; TimerMgr_t tmgr = (TimerMgr_t)arg; if(total == 20) { RemoveTimer(tmgr,t); DestroyTimer(&t); TerminateTimerMgr(tmgr); } } int main() { TimerMgr_t t = CreateTimerMgr(); Timer_t _timer = DEFAULT_TIMER(500,test_callback,(void*)t); AddTimer(t,_timer); RunTimerMgr(t); DestroyTimerMgr(&t); return 0; }
默认的DefaultInit只提供了初始化固定间隔定时器的功能,只需要提供合适的初始化函数便可实现
整点报时,闹钟等定时器。