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

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

  • 相关阅读:
    第三次作业
    利用LINQ to SQL 增删改查本地数据库
    【转】C#,回车,换行
    【转】C#3.0编码习惯与命名规则
    Connect to the DSP on C6A8168/DM8168/DM8148 using CCS
    【转】OpenCV 移植学习EMCV
    目标跟踪小结
    利用TortoiseSVN获取最新版本的OpenCV源码
    转图像灰度化方法总结及其VC实现
    OpenCV读取视频的格式注意点
  • 原文地址:https://www.cnblogs.com/sniperHW/p/2459441.html
Copyright © 2011-2022 走看看