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只提供了初始化固定间隔定时器的功能,只需要提供合适的初始化函数便可实现

整点报时,闹钟等定时器。

posted @ 2012-04-20 15:57  sniperHW  阅读(1868)  评论(5编辑  收藏  举报