基于setitimer实现允许单进程多次调用的定时器

来源:互联网 发布:淘宝创业网址是什么 编辑:程序博客网 时间:2024/06/09 20:33

原理

setitimer不能在进程中多次调用,考虑用链表管理调用。每个节点包含当前事件还剩余多少时间,回调等,每次有新节点时,重新更新节点时间。在计数到来后,重新恢复节点初始时间实现重复计时,删除相对简单,挪掉节点即可。

函数原型

int set_timer(struct own_timer **t, struct timeval val, callback* cb, void* arg) ;int free_timer(struct own_timer* timer) ;

源码

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <signal.h>#include <errno.h>#include <sys/time.h>#include <pthread.h>#define MIN(a,b) (((a) < (b))?(a):(b))typedef void (*callback)(void* argc);typedef struct list {    void* next;    void* prev;} LIST_ENTRY;struct own_timer {    LIST_ENTRY entry;    int handle;    struct timeval left_dyn;    struct timeval left_init;    callback* cb;    void* cb_arg;};static LIST_ENTRY *head = NULL;static LIST_ENTRY *tail = NULL;static struct timeval last_set;static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;static int test = 0;static struct own_timer *t1 = NULL;static struct own_timer *t2 = NULL;static struct own_timer *t3 = NULL;void timeout(void* arg){    printf("timeout %d >>\n",(int)arg);}void timeout4(void* arg){    printf("timeout4 >>>>>>>>>\n");    test ++;}static int compare_time(struct timeval a, struct timeval b){    if(a.tv_sec > b.tv_sec)        return 1;    else if(a.tv_sec == b.tv_sec && a.tv_usec > b.tv_usec)         return 1;    else if(a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec)         return 0;    else        return -1;}static int diff_time(struct timeval* result, struct timeval* old,struct  timeval* newt){    if(compare_time(*old,*newt) < 0) {        //printf("old shoud bigger\n");        return -1;    }    if((old->tv_sec - newt->tv_sec) >= 1) {        old->tv_sec --;        old->tv_usec += 1000000;    }    result->tv_sec = old->tv_sec - newt->tv_sec;    result->tv_usec = old->tv_usec - newt->tv_usec;    if(result->tv_usec >= 1000000) {        result->tv_usec -= 1000000;        result->tv_sec ++;    }    return 0;}void sig_alarm(int sig){    if(sig == SIGALRM) {        LIST_ENTRY* tmp;        struct own_timer* ptr =NULL;        struct own_timer* ptr_min2nd = NULL;        struct timeval min;        struct timeval min_2nd;        callback* min_cb;        void* min_arg;        //printf("lock in sig_alarm\n");        pthread_mutex_lock(&mutex);        //printf("SIG_ALARM\n");        //遍历结点,找到left时间最小的, 可能有多个.一定是它发生了        if (head == NULL) {            //printf("free in sig_alarm\n");            pthread_mutex_unlock(&mutex);            //printf("no nodes in list\n");            return;         } else {            //printf("find min...\n");            tmp = head->next;            ptr = (struct own_timer*)tmp;            min = ptr->left_dyn;            do {                ptr = (struct own_timer*)tmp;                //printf("min(%d %d) cur(%d %d)\n",(int)min.tv_sec,(int)min.tv_usec,(int)ptr->left_dyn.tv_sec,(int)ptr->left_dyn.tv_usec);                if(compare_time(min,ptr->left_dyn) > 0) {                    min = ptr->left_dyn;                }                tmp = tmp->next;            }while(tmp != tail);            pthread_t tid;            tmp = head->next;            ptr = (struct own_timer*)tmp;            min_2nd = ptr->left_dyn;            ptr_min2nd = ptr;            //printf("excute min and find 2nd min ...\n");            do {                ptr = (struct own_timer*)tmp;                diff_time(&ptr->left_dyn,&ptr->left_dyn,&min);                //printf("left_dyn(%d %d)\n",(int)ptr->left_dyn.tv_sec,(int)ptr->left_dyn.tv_usec);                if (ptr->left_dyn.tv_usec == 0 && ptr->left_dyn.tv_sec == 0) {                    //printf("handle = %x\n",ptr->handle);                    //回调                    if(ptr->cb) {                        pthread_create(&tid,NULL,ptr->cb,ptr->cb_arg);                        //回调里可能链表已经释放了                    }                    //重新计时                    ptr->left_dyn = ptr->left_init;                    //printf("reset left_dyn(%d %d)\n",(int)ptr->left_dyn.tv_sec,(int)ptr->left_dyn.tv_usec);                }                //不等于0的节点中寻找最小的                if(ptr->left_dyn.tv_sec != 0 || ptr->left_dyn.tv_usec != 0) {                    if(compare_time(min_2nd,ptr->left_dyn) > 0) {                        min_2nd = ptr->left_dyn;                        ptr_min2nd = ptr;                    }                }                tmp = tmp->next;            }while(tmp != tail);        }        //开始min_2nd setitimer        //printf("min_2nd (%d %d)\n",(int)ptr_min2nd->left_dyn.tv_sec,(int)ptr_min2nd->left_dyn.tv_usec);        struct itimerval newt;        struct timeval interval;        interval.tv_sec = 0;        interval.tv_usec = 0;        newt.it_value = ptr_min2nd->left_dyn;        newt.it_interval = interval;        setitimer(ITIMER_REAL,&newt,NULL);        last_set = ptr_min2nd->left_dyn;    }    //printf("free in sig_alarm\n");    pthread_mutex_unlock(&mutex);}int free_timer(struct own_timer* timer) {    LIST_ENTRY* tmp;    LIST_ENTRY* tmp_prev;    LIST_ENTRY* tmp_next;    struct own_timer* ptr;    tmp = head->next;    ptr = (struct own_timer*)tmp;    //printf("lock in free_timer\n");    pthread_mutex_lock(&mutex);    do {        ptr = (struct own_timer*)tmp;        if (timer->handle == ptr->handle) {            //printf("found the node timer need free\n");            tmp_prev = tmp->prev;            tmp_next = tmp->next;            tmp_prev->next = tmp->next;            tmp_next->prev = tmp->prev;            break;        }        tmp = tmp->next;    } while(tmp != tail);#if 1    if(timer) free(timer);    if(head->next == tail) {        if(head) {            free(head);            head = NULL;        }        if(tail) {            free(tail);            tail = NULL;        }     }#endif    //printf("free in free_timer\n");    pthread_mutex_unlock(&mutex);    return 0;}int set_timer(struct own_timer **t, struct timeval val, callback* cb, void* arg) {    struct itimerval newt;    struct itimerval oldt;    struct timeval set,left,passed;    struct timeval interval;    LIST_ENTRY* tmp;    struct own_timer* ptr =NULL;    sigset_t news,olds;    //节点内存分配    (*t) = (struct own_timer*)malloc(sizeof(struct own_timer));    if(*t == NULL) {        perror("malloc fail");        return -1;    }       (*t)->left_dyn = (*t)->left_init = val;    (*t)->cb = cb;    (*t)->cb_arg = arg;    (*t)->handle = 0xFFFFFFFF & rand();    //printf("new handle = %x\n",*t->handle);    //屏蔽SIGALRM ,比较粗糙    sigemptyset(&news);    sigaddset(&news,SIGALRM);    sigprocmask(SIG_BLOCK,&news,&olds);    //锁    //printf("lock in set_timer\n");    pthread_mutex_lock(&mutex);    interval.tv_sec = 0;    interval.tv_usec =0;    newt.it_interval = interval;    //获得从上次setitimer到现在,计时器还剩多少时间.    getitimer(ITIMER_REAL,&oldt);    left = oldt.it_value;    //刚开始    if(left.tv_sec == 0 && left.tv_usec == 0 && head == NULL) {        //printf("setitimer 1st start\n");        newt.it_value = val ;        setitimer(ITIMER_REAL,&newt,NULL);        last_set = val;        head = (LIST_ENTRY*)malloc(sizeof(LIST_ENTRY));        tail = (LIST_ENTRY*)malloc(sizeof(LIST_ENTRY));        //插入第1个节点        head->next = &((*t)->entry);        (*t)->entry.prev = head;        (*t)->entry.next = tail;        tail->prev = *t;        head->prev = tail;        tail->next = head;    } else if(left.tv_sec == 0 && left.tv_usec == 0 && head != NULL) {         //printf("setitimer timer stop\n");        newt.it_value = val ;        setitimer(ITIMER_REAL,&newt,NULL);        last_set = val;        tmp = head->next;        do {            tmp = tmp->next;        }while(tmp != tail);        //顺序插入节点        tmp = tmp->prev;        tmp->next = &((*t)->entry);        (*t)->entry.prev = tmp;        (*t)->entry.next = tail;        tail->prev = *t;    } else {        //printf("setitimer timer running\n");        //换算成消耗的时间. last_set是在setitimer后保存的timeval        diff_time(&passed,&last_set,&left);         //printf("passed time(%d %d)\n",(int)passed.tv_sec,(int)passed.tv_usec);        //剩余时间比当前想设置的计时大,重设置计时为当前计时。        if(compare_time(left,val) >= 0 ) {            newt.it_value = val ;            setitimer(ITIMER_REAL,&newt,NULL);        }         //每个结点需要减已消耗的时间。        tmp = head->next;        do {            ptr = (struct own_timer*)tmp;            //printf("handle %x:\n",ptr->handle);            //printf("before diff left_dyn(%d %d)\n",(int)ptr->left_dyn.tv_sec,(int)ptr->left_dyn.tv_usec);            diff_time(&ptr->left_dyn,&ptr->left_dyn,&passed);            //printf("after diff left_dyn(%d %d)\n",(int)ptr->left_dyn.tv_sec,(int)ptr->left_dyn.tv_usec);            tmp = tmp->next;        }while(tmp != tail);        //尾部插入节点        tmp = tmp->prev;        tmp->next = &((*t)->entry);        (*t)->entry.prev = tmp;        (*t)->entry.next = tail;        tail->prev = *t;    }    //恢复    sigprocmask(SIG_SETMASK,&olds,NULL);    //解锁    //printf("free in set_timer\n");    pthread_mutex_unlock(&mutex);    //printf("recover SIGALRM\n");    return 0;}void* thread_set_timer(void* arg){    struct own_timer *timer;    struct timeval val;    val.tv_sec = 0;    val.tv_usec = 500000;    set_timer(&timer, val, timeout4, NULL); }int main (int argc, char* argv[]){    struct timeval val;    signal(SIGALRM,sig_alarm);    int i = 0;    for (i = 1;i <= 500; i++) {        val.tv_sec = i;        val.tv_usec = 0;        set_timer(&t1, val, timeout, i);     }    //pthread_t tid;    //pthread_create(&tid,NULL,thread_set_timer,NULL);    while(1) {#if 0        printf("test = %d\n",test);        if(test == 10) {            free_timer(t2);            free_timer(t3);        }        if(test == 15) {            free_timer(t1);            set_timer(&t2, val, timeout2, NULL);             set_timer(&t3, val, timeout3, NULL);         }#endif        sleep(1);    }    return 0;}

运行结果

timeout 1 >>
timeout 2 >>
timeout 3 >>
timeout 1 >>
timeout 4 >>
timeout 1 >>
timeout 2 >>
timeout 5 >>

后续考虑优化

1 不使用信号

setitimer利用SIGALARM信号,期间不能使用sleep()等系统调用,用相同功能的函数代替之,比如用sleep+thread实现一个setitimer,自己实现回调。
要求严格计时,在主线程里的主回调相当与sig_alarm.这个线程的优先级应该最高保证每次计时完成时,都能及时调用主回调。

2 数据结构

双向链表在大量定时器申请时,并无优势,可换成最小堆。

3 原理改进

考虑时间轮 并且tick分级。比如 某次定时需要5555个tick 每轮1000 需要 5*1000 +5*100 + 5*10 +5个tick。在不同时间轮上,计数不同片,最后等待轮最大的完成即可。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 一年级的孩子数学不好怎么办 小学一年级孩子数学不好怎么办 孩子上一年级数学太差怎么办 智商低情商也低怎么办? 孩子字写得很大怎么办 孩子拿笔重 写字太黑 怎么办 孩子语文成绩好数学不行怎么办? 孩子现在二年级特别叛逆怎么办 孩子又笨又蠢怎么办 四年级孩子数学计算能力差怎么办 孩子四年级数学理解能力差怎么办 孩子小学四年级数学很差怎么办 孩子做作业太慢怎么办 小学三年级数学成绩差怎么办 初一数学考了3分怎么办 初二物理太差该怎么办 三年级孩子字写的差怎么办 小学三年级数学才考86怎么办 小孩子一发脾气就打妈妈怎么办 孩子做错事不肯道歉怎么办 小孩写作业注意力不集中怎么办 六年级的数学下册差怎么办 一年级小孩做作业慢怎么办 静不下心写作业怎么办 二年级应用题太差怎么办 小学二年级数学差怎么办 小学二年级成绩差怎么办 6个月小孩爱动怎么办 儿子叛逆期我该怎么办 宝宝两岁好动不听话怎么办 生宝宝后奶水少怎么办 生了孩子没出来怎么办 孩子在学校表现不好怎么办 3岁半宝宝话太多怎么办 孩子不喜欢和小朋友玩怎么办 孩子不喜欢和小朋友说话怎么办 4岁半宝宝不听话怎么办 小孩在学校打老师怎么办 老师老找孩子时怎么办 幼儿园老师批评孩子后家长怎么办 老师跟家长吵架了怎么办