基于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
- 基于setitimer实现允许单进程多次调用的定时器
- Linux setitimer()在同一进程中实现多个定时器
- linux定时器 --setitimer 的用法
- setitimer定时器的简单使用
- 多次调用定时器
- setitimer 定时器
- Linux setitimer()函数实现单个定时器
- 编写一个程序库,实现定时器的功能,它能为用户提供在同一进程中多次使用的定时器。
- Linux下的定时器:alarm()与setitimer()
- Linux下的定时器:alarm()与setitimer()
- Linux下的定时器:alarm()与setitimer()
- Linux下的定时器:alarm()与setitimer()
- Linux下的定时器:alarm()与 setitimer()
- Linux下的定时器:alarm()与setitimer()
- Linux下的定时器:alarm()与setitimer()
- linux下定时器的使用 - alarm() & setitimer()
- Linux下的定时器:alarm()与setitimer()
- Linux下的定时器:alarm()与setitimer()
- 基于Django 文档1.11 自解+补完 学习django ---part1
- 【Android View基础】View中几个容易混淆的距离及坐标量
- 下载并安装apk文件
- 简单的文本处理
- java日期类总结
- 基于setitimer实现允许单进程多次调用的定时器
- 程序的内存分配
- python 的subprocess学习记录
- WAV文件格式解析
- 基于DHT11温湿度传感器的Android硬件访问服务的简单实现(一)
- ajax问题总结
- Uva 11178 Morley's Theorem (几何+模拟)
- bio、nio 、aio介绍
- jquery中ajax的dataType属性包括哪几项