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只提供了初始化固定間隔定時器的功能,只需要提供合適的初始化函數便可實現

整點報時,鬧鍾等定時器。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM