日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux 编程--三种常用的定时器

發布時間:2025/3/21 linux 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux 编程--三种常用的定时器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這節我們來探討一下linux開發過程中常用的定時器,尤其在網絡編程中被常常用到如heartbeat,斷線重連等等。這里提供了三種定時器的方案,分別是鏈表形式的計時器,環型計時器,最小堆計時器。每個都有不同的作用和優勢,可以結合實際項目選擇或者改良。

鏈表計時器:

鏈表計時器是一個實現很簡單的一種計時器,可以使用單鏈表或者雙鏈表來實現,我這里有一個雙鏈表實現的例子

/*** timer list******/ #ifndef LIST_TIMER_H #define LIST_TIMER_H #include <time.h>typedef struct util_time{struct util_time * prev; // 雙鏈表的前驅struct util_time * next; //雙鏈表后驅//Client data pointvoid * cdata; //其他數據//timeout valuetime_t out_time; //定時時間int persist; //是否是堅持定時//if timeout callbackvoid (*timeout_callback)(void * data); //定時回調函數 }UTIL_TIME;struct list_timer;struct TimerOP {int (*add)(struct list_timer * lt, UTIL_TIME * ut);int (*del)(struct list_timer * lt, UTIL_TIME ** ut);int (*adjust)(struct list_timer * lt, UTIL_TIME * ut, time_t _time);void (*_tick)(struct list_timer * lt); };//Timer list //定時器鏈表頭 typedef struct list_timer {struct TimerOP timer_op;UTIL_TIME * head;UTIL_TIME * tail; }LIST_TIMER;//timer list operator, all function only handle list and free timer, not free clientdata int add_timer(LIST_TIMER * lt, UTIL_TIME * ut); int del_timer(LIST_TIMER * lt, UTIL_TIME **ut); int adjust_timer(LIST_TIMER * lt, UTIL_TIME * ut, time_t _time); void tick(LIST_TIMER * lt); void init_List_Timer(LIST_TIMER * lt); void destroy_list_Timer(LIST_TIMER * lt); #endif View Code #include <stdlib.h> #include <string.h> #include <signal.h> #include <unistd.h> #include <stdio.h> #include "list_timer.h"/*** @brief init_List_Timer* @param lt timer list* initialize list timer, head and tail are NULL* 初始化定時器*/ void init_List_Timer(LIST_TIMER *lt) {lt->timer_op.add = add_timer;lt->timer_op.del = del_timer;lt->timer_op.adjust = adjust_timer;lt->timer_op._tick = tick;lt->head = NULL;lt->tail = NULL; }/*** @brief add_timer* @param lt* @param ut* @return* 添加一個定時器 并跟定時器排序*/ int add_timer(LIST_TIMER * lt, UTIL_TIME * ut) {if (lt && ut){if (!lt->head && !lt->tail){lt->head = ut;lt->tail = ut;ut->prev = NULL;ut->next = NULL;return 1;}else if (lt->head && lt->tail){UTIL_TIME * temp = lt->head;UTIL_TIME * tempbak = NULL;while(temp){if((temp->out_time) > (ut->out_time)){if(lt->head == temp){ut->next = temp;temp->prev = ut;lt->head = ut;ut->prev = NULL;return 1;}}tempbak = temp;temp = temp->next;}tempbak->next = ut;ut->prev = tempbak;ut->next = NULL;lt->tail = ut;return 1;}return 0;}return 0; }/*** @brief del_timer* @param lt* @param ut* @return* 由相應定時器的位置刪除定時器*/ int del_timer(LIST_TIMER * lt, UTIL_TIME **ut) {if(lt && ut && *ut){if(lt->head){UTIL_TIME *temp = *ut;if((temp == lt->head) && (temp != lt->tail)) // {lt->head = temp->next;temp->next->prev = NULL;}else if((temp == lt->tail) && (temp != lt->head)) // {temp->prev->next = NULL;lt->tail = temp->prev;}else if((temp == lt->tail) && (temp == lt->head)) //只有一個定時器 {lt->head = NULL;lt->tail = NULL;}else{temp->next->prev = temp->prev;temp->prev->next = temp->next;}(*ut)->cdata = NULL;(*ut)->next = NULL;(*ut)->prev = NULL;(*ut)->timeout_callback = NULL;free(*ut);*ut = NULL;ut = NULL;return 1;}}return 0; }/*** @brief adjust_timer* @param lt* @param ut* @param _time* @return* if a timer be deleted or addition time to a timer, adjust timer list* 移除指針并插入*/ int adjust_timer(LIST_TIMER * lt, UTIL_TIME * ut, time_t _time) {if(!lt || !ut){return 0;}ut->out_time = time(NULL) + _time;if(!ut->prev && !ut->next){return 1; //only have single Node}else if (ut->prev && !ut->next) {ut->prev->next = NULL; //if ut is tail Node, remove it.ut->prev = NULL;}else if (!ut->prev && ut->next) {lt->head = ut->next; //if ut is head Nodeut->next->prev = NULL;ut->next = NULL;ut->prev = NULL;}else{ut->next->prev = ut->prev; //ut is middleut->prev->next = ut->next;ut->next = NULL;ut->prev = NULL;}// Can be optimized , insert after this Node.if(add_timer(lt, ut)) //reinsert it {return 1;}return 0; }/*** @brief tick* @param lt* Timer list tick, depend on callback function, adjust timer.*/ void tick(LIST_TIMER * lt) {if(lt){time_t now = time(NULL);UTIL_TIME *temp = lt->head;UTIL_TIME *tempbak = temp;while(temp){tempbak = temp;temp = temp->next;if(tempbak->out_time <= now) //檢查時間 {tempbak->timeout_callback(tempbak->cdata);if(!(tempbak->persist)) del_timer(lt, &tempbak);else{//persist time 重新調整adjust_timer(lt, tempbak, tempbak->persist);}}else{break;}}return;}return; }/*** @brief destroy_list_Timer* @param lt* destroy timer list*/ void destroy_list_Timer(LIST_TIMER *lt) {if(!lt){return;}UTIL_TIME * temp = lt->head;UTIL_TIME * tempnext = NULL;while(temp){tempnext = temp->next;temp->cdata = NULL;temp->next = NULL;temp->prev = NULL;temp->timeout_callback = NULL;free(temp);temp = tempnext;}lt->head = NULL;lt->tail = NULL; } View Code

測試用例

#include "list_timer.h" #include <stdio.h> #include <unistd.h> #include <signal.h> #include <stdlib.h>enum {CONNECTED, READ, WROTE, NORMAL};typedef struct ClientData{int fd;char ipaddr[4];char * data;//This client monitor statusint flags;//Timer point, if ut is null that mean not included into timer list.struct util_time * ut; }CLIENTDATA; LIST_TIMER lt;void doit(void * mydata) {CLIENTDATA * data = (CLIENTDATA *)mydata;if(data){switch(data->flags){case CONNECTED:fprintf(stderr, "don`t delete\n");break;case READ:fprintf(stderr, "delete \n");break;case WROTE:break;case NORMAL:break;default:break;}} }int main() {init_List_Timer(&lt);CLIENTDATA * cl1 = (CLIENTDATA *)malloc(sizeof(CLIENTDATA));cl1->data = NULL;cl1->fd = 23;cl1->flags = CONNECTED;CLIENTDATA * cl2 = (CLIENTDATA *)malloc(sizeof(CLIENTDATA));cl2->data = NULL;cl2->fd = 2324;cl2->flags = READ;UTIL_TIME *ut2 = (UTIL_TIME *)malloc(sizeof(UTIL_TIME));ut2->timeout_callback = doit;ut2->cdata = cl2;ut2->out_time = time(NULL) + 2;ut2->persist = 0;UTIL_TIME *ut1 = (UTIL_TIME *)malloc(sizeof(UTIL_TIME));ut1->timeout_callback = doit;ut1->cdata = cl1;ut1->out_time = time(NULL)+ 6;ut1->persist = 10;add_timer(&lt, ut1);add_timer(&lt, ut2);while(1){tick(&lt);usleep(500);}return 0; } View Code

?

list_timer.c list_timer.h 其中list_main.c為測試實例。 其中的tick函數? 可以放在一個死循環中,這樣就實現了簡單的定時,每個定時器按照事件間隔的從小到大的順序并使用雙鏈表進行組織,若定時器為persist的話就反復調整計時器,加上每次間隔時間并重新調整鏈表。在tick中比對頭個計時器的時間如果小于目前時間就執行相應回調函數。

如在epoll模型的服務器中若要加入某個定時器,可以這樣做

while(1) {tick(timer); epoll_wait(); //此處得到定時器的最短計時, 即雙鏈表表頭定時間隔作為 阻塞時間。//其他for(;;){}}

此種定時器的效率跟鏈表操作相同插入 刪除都是logN,調整計時器在鏈表的位置花費較多的時間(去掉重新插入)。

環形定時器:

這種定時器就像鐘表一樣,如秒針一樣一秒移動一次,當秒針移動到相應位置時,查看此位置是否有定時器,若有就不斷的check時間。這里的某個指針位置的定時器可以以鏈表形式由圈數量從小到大排列。指針走到某個位置查看鏈表的圈數若為0則表示時間到了 否則將圈數減1等待下次調用。如圖所示:

這里給出實例代碼如下:

#ifndef ROUND_TIMER_H #define ROUND_TIMER_H #include <time.h>typedef struct aw_timer{void *data;struct aw_timer * prev;struct aw_timer * next;time_t expect;//current slot numberint slotnumber;//timer loop cercle numberint loopcercle;//weather persistint persist;//if return 0 : mean don`t delete timer//return 1 : delete timer.void (*timeout_callback)(void * data); }AW_TIMER;#define N 60 typedef struct wh_timers{//time gap 1 sec or 1 min or 1 hourint timegap;//current slot point locetionint curslotpoint;//timer list pointAW_TIMER *slot[N]; }WH_TIMERS;void init_wh_timer(WH_TIMERS * wt, int timegap); int add_wh_timer(WH_TIMERS * wt, AW_TIMER *at); int del_wh_timer(WH_TIMERS * wt, AW_TIMER **at, int remove); int adjust_wh_timer(WH_TIMERS * wt, AW_TIMER *at); void wh_tick(WH_TIMERS * wt); void destory_wh_timer(WH_TIMERS * wt);#endif View Code #include <time.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <signal.h> #include "round_timer.h"/*** @brief insert_sort_timer* @param aw_list* @param at* sort timer depend on loopcercle* 按照圈數插入*/ static void insert_sort_timer(AW_TIMER ** aw_list, AW_TIMER **at) {if(aw_list && at){AW_TIMER * atr = *aw_list;while(atr){if(((*at)->loopcercle <= (atr->loopcercle)) && (!atr->prev)){(*at)->next = atr;atr->prev = *at;(*at)->prev = NULL;*aw_list = *at;return;}else if(((*at)->loopcercle >= (atr->loopcercle)) && (!atr->next)) {atr->next = *at;(*at)->prev = atr;(*at)->next = NULL;return;}else if(((*at)->loopcercle <= (atr->loopcercle)) && atr->next && atr->prev){(*at)->next = atr;(*at)->prev = atr->prev;atr->prev->next = *at;atr->prev = *at;return;}atr = atr->next;}} }/*** @brief init_wh_timer* @param wt* @param timegap* Initialize wheel timers, timegap mean seconds one step.* If timegap < 0 , timegap = 1*/ void init_wh_timer(WH_TIMERS * wt, int timegap) {if(wt){wt->curslotpoint = 0;wt->timegap = timegap ? timegap : 1;int i;for(i = 0; i < N; ++i){wt->slot[i] = NULL;}} }/*** @brief add_wh_timer* @param wt* @param at* @return* add a timer into wheel timers*/ int add_wh_timer(WH_TIMERS *wt, AW_TIMER *at) {if(wt && at){if(at->expect){//get timer loop cercles.at->loopcercle = (at->expect) / ((wt->timegap) * N);//get timer slot number in wheelat->slotnumber = (wt->curslotpoint + ((at->expect) / (wt->timegap))) % N;int index_t = at->slotnumber;if(wt->slot[index_t]){//If this slot is not empty, insert it sortable.insert_sort_timer(&(wt->slot[index_t]), &at);}else{wt->slot[index_t] = at;at->prev = NULL;at->next = NULL;}}} }/*** @brief del_wh_timer* @param wt* @param at* @param remove* @return* delete a timer, and the remove flag mean weather free it or not.*/ int del_wh_timer(WH_TIMERS *wt, AW_TIMER **at, int remove) {if(wt && at){if(*at){if(!((*at)->prev)){wt->slot[(*at)->slotnumber] = (*at)->next;if((*at)->next)(*at)->next->prev = NULL;}else if(!((*at)->next)){if((*at)->prev)(*at)->prev->next = NULL;elsewt->slot[(*at)->slotnumber] = NULL;}else{(*at)->prev->next = (*at)->next;(*at)->next->prev = (*at)->prev;(*at)->prev = NULL;(*at)->next = NULL;}//是否真的移除if(remove){free(*at);*at = NULL;}}return 0;}return 0; }/*** @brief adjust_wh_timer* @param wt* @param at* @return* reset timer, fristly, will remove timer, then insert wheel again.*/ int adjust_wh_timer(WH_TIMERS *wt, AW_TIMER *at) {//調整計時器if(wt && at){del_wh_timer(wt, &at, 0);add_wh_timer(wt, at);} }/*** @brief wh_tick* @param wt* point move step by step, if slot is not null, run timeout callback function.* if loop cercle sum > 0, will skip it.*/ void wh_tick(WH_TIMERS *wt) {if(wt){wt->curslotpoint = (wt->curslotpoint + 1)%N;if(wt->slot[wt->curslotpoint]){AW_TIMER * at = wt->slot[wt->curslotpoint];AW_TIMER *attemp = at;while(at){attemp = at->next;if(0 >= at->loopcercle){//圈數為0 開始調用at->timeout_callback(at->data);if(at->persist){adjust_wh_timer(wt, at);}else{del_wh_timer(wt, &at, 1);}}else{//等待下次被調 并停止循環(at->loopcercle)--;break;}at = attemp;}}alarm(wt->timegap);} }/*** @brief destory_wh_timer* @param wt* free timer wheel.*/ void destory_wh_timer(WH_TIMERS *wt) {int i;for(i = 0; i < N; ++i){if(wt->slot[i]){AW_TIMER * timer = wt->slot[i];AW_TIMER * temptimer = timer;while(timer){temptimer = timer->next;free(timer);timer = NULL;timer = temptimer;}temptimer = NULL;wt->slot[i] = NULL;}} } View Code #include "list_timer.h" #include <stdio.h> #include <unistd.h> #include <signal.h> #include <stdlib.h>enum {CONNECTED, READ, WROTE, NORMAL};typedef struct ClientData{int fd;char ipaddr[4];char * data;//This client monitor statusint flags;//Timer point, if ut is null that mean not included into timer list.struct util_time * ut; }CLIENTDATA; LIST_TIMER lt;void doit(void * mydata) {CLIENTDATA * data = (CLIENTDATA *)mydata;if(data){switch(data->flags){case CONNECTED:fprintf(stderr, "don`t delete\n");break;case READ:fprintf(stderr, "delete \n");break;case WROTE:break;case NORMAL:break;default:break;}} }int main() {init_List_Timer(&lt);CLIENTDATA * cl1 = (CLIENTDATA *)malloc(sizeof(CLIENTDATA));cl1->data = NULL;cl1->fd = 23;cl1->flags = CONNECTED;CLIENTDATA * cl2 = (CLIENTDATA *)malloc(sizeof(CLIENTDATA));cl2->data = NULL;cl2->fd = 2324;cl2->flags = READ;UTIL_TIME *ut2 = (UTIL_TIME *)malloc(sizeof(UTIL_TIME));ut2->timeout_callback = doit;ut2->cdata = cl2;ut2->out_time = time(NULL) + 2;ut2->persist = 0;UTIL_TIME *ut1 = (UTIL_TIME *)malloc(sizeof(UTIL_TIME));ut1->timeout_callback = doit;ut1->cdata = cl1;ut1->out_time = time(NULL)+ 6;ut1->persist = 10;add_timer(&lt, ut1);add_timer(&lt, ut2);while(1){tick(&lt);usleep(500);}return 0; } View Code

我們可以看到在例子中 我用了定時信號來模擬一秒走一次,我們注意到這里信號可能會造成某些系統調用的中斷 這里可以將它該為類似鏈表計時器中那樣,在循環中得到倆次時間間隔 若大于等于1s就將秒針加1, 不過這樣可能就不太準確了。可以將時間間隔設置的稍微大一些。

最小堆計時器:

最小堆計時器是比較常用的一種計時器,在libevent中可以看到它的使用。這種數據結構每次返回的是最小值時間間隔定時器。Libevent 事件循環(1)? 這里可以簡單看一下它的用法。當添加計時器時,就不斷調整計時器在堆中的位置保證堆頂是一個最小計時。當計時器從堆中刪除時就不斷的向下不斷找最小的計時器放在堆頂,至于最小堆的實現這里有一篇不錯的文章分享給大家 : 二叉堆(一)之 圖文解析 和 C語言的實現

我這里給出一個實例代碼:

#include <time.h> typedef struct {int persist; //是否堅持time_t timeout; //timeout = 時間間隔 + 現在時間time_t timegap; //時間間隔void * data; //用戶數據 }Timer; typedef struct {Timer *timer;size_t size;size_t capacity; }HeapTimer; //動態數組 ?最小堆HeapTimer * initHeapTimer(size_t s); void min_heap_push(HeapTimer * ht, Timer * _timer); void min_heap_pop(HeapTimer * ht, Timer * _timer); int check_had_timeout(HeapTimer *ht, time_t now); #include <stdlib.h> #include <stdio.h> #include <string.h> #include "heap_time.h" //添加計時器后 需要不斷上調二茶堆 ?保證堆頂最小 static void flow_up(HeapTimer *ht, Timer *_timer) {if (ht && _timer){if (ht->size == 0){return;}else if (ht->size == 1){memcpy(&(ht->timer[ht->size]), _timer, sizeof(Timer));_timer = NULL;return;}int temp = ht->size;while(temp){
        //與父節點比較
if (ht->timer[temp/2].timeout > (_timer->timeout)){ht->timer[temp].timeout = ht->timer[temp/2].timeout;ht->timer[temp].data = ht->timer[temp/2].data;ht->timer[temp].persist = ht->timer[temp/2].persist;ht->timer[temp].timegap = ht->timer[temp/2].timegap;}else{break;}temp = temp/2;}memcpy(&(ht->timer[temp]), _timer, sizeof(Timer));_timer = NULL;} } //刪除堆頂不斷調整 static void flow_down(HeapTimer *ht) {unsigned int start = 1;unsigned int end = ht->size;unsigned int current = start;unsigned int l = 2*current;while(l <= end){
    //當節點存在右孩子,比較左右兩個孩子找到最小的一個
if (l < end && (ht->timer[l+1].timeout < ht->timer[l].timeout)){l++;}
? ? ? ? //拿最后一個與上次比較最小的一個比較 如果比它小就停止將最后一個放到此位置
if (ht->timer[end].timeout < ht->timer[l].timeout){break;}else{
? ? ? ? ? ? //將最小的上浮繼續比對ht
->timer[current].timeout = ht->timer[l].timeout;ht->timer[current].data = ht->timer[l].data;ht->timer[current].persist = ht->timer[l].persist;ht->timer[current].timegap = ht->timer[l].timegap;current = l;l = 2*current;}}ht->timer[current].timeout = ht->timer[end].timeout;ht->timer[current].data = ht->timer[end].data;ht->timer[current].persist = ht->timer[end].persist;ht->timer[current].timegap = ht->timer[end].timegap; }HeapTimer * initHeapTimer(size_t s) {if (s <= (size_t)0){return NULL;}HeapTimer * ht = (HeapTimer *)malloc(sizeof(HeapTimer));if (!ht){return NULL;}ht->size = 0;ht->capacity = s+1;ht->timer = (Timer *)malloc(sizeof(Timer) * (s+1));if (ht->timer){memset(ht->timer, 0, sizeof(Timer)*(s+1));return ht;}return NULL; } //插入一個定時器 void min_heap_push(HeapTimer * ht, Timer * _timer) {if (ht && _timer){if (ht->size == ht->capacity){size_t need = ht->size + (ht->size)/3;Timer *temp = (Timer *)malloc(sizeof(Timer)*(need));if(temp){memset(temp, 0, sizeof(Timer)*(need));memcpy(temp, ht->timer, sizeof(ht->size));ht->capacity = need;free(ht->timer);ht->timer = temp;temp = NULL;}}(ht->size)++;flow_up(ht, _timer);} } //刪除一個定時器 void min_heap_pop(HeapTimer * ht, Timer * _timer) {if ( ht && ht->size){memset(_timer, 0, sizeof(Timer));memcpy(_timer, &(ht->timer[1]), sizeof(Timer));ht->timer[1].timeout = ht->timer[ht->size].timeout;ht->timer[1].data = ht->timer[ht->size].data;ht->timer[1].persist = ht->timer[ht->size].persist;ht->timer[1].timegap = ht->timer[ht->size].timegap;if (ht->size > 1){flow_down(ht);}ht->timer[ht->size].timeout = 0;ht->timer[ht->size].data = NULL;ht->timer[ht->size].persist = 0;ht->timer[ht->size].timegap = 0;(ht->size)--;} } //檢查是否超時 int check_had_timeout(HeapTimer *ht, time_t now) {if (ht){if (ht->size > (size_t)0){return (ht->timer[1]).timeout < now;}}return 0; } //為測試使用 int main() {HeapTimer *timer = initHeapTimer(50);Timer t1;t1.timegap = 3;t1.timeout = time(NULL) + t1.timegap;t1.persist = 1;Timer t2;t2.timegap = 6;t2.timeout = time(NULL) + 6;t2.persist = 0;min_heap_push(timer, &t1);min_heap_push(timer, &t2);while(1){if (check_had_timeout(timer, time(NULL))) //不斷檢查和調整{Timer temp;min_heap_pop(timer, &temp); ? //if (temp.persist){temp.timeout = time(NULL) + temp.timegap;min_heap_push(timer, &temp);}printf("timeout %u\n", timer->size);}usleep(100);}return 0; }

堆計時器的效率還是可以的,插入刪除logN。目前在網絡編程中 最小堆計時常用到。

總結:

綜上所述這三種常用的計時器做了簡單介紹,其中實例代碼仍有許多改進之處,存在不足之處敬請見諒及反饋,可以在github上找到: https://github.com/BambooAce/MyEvent/tree/master/src比較這三種定時器,一般在網絡編程中較推薦的是鏈表計時器和最小堆計時器,在最新的libevent中也同時提供了這兩種計時器可供選擇。在網絡編程中不乏缺少心跳檢測,當客戶端數量十分龐大時,小根堆定時器的好處就很明顯了。

?

轉載于:https://www.cnblogs.com/MaAce/p/8005723.html

總結

以上是生活随笔為你收集整理的Linux 编程--三种常用的定时器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。