跟上一篇,這裏寫一下時間堆:
時間輪的滴答是固定以指定的槽間隔觸發,而時間堆是以定時器堆中的最小到期時間做定時,也就是alarm(minTimeout),一旦定時器被觸發,那麼就刪除此定時器,更新時間堆,將新的最小到期時間定時器作爲新的定時。
然後是時間堆的數據結構,這裏用了二叉樹,將二叉樹的根節點作爲最小最小到期時間,每次滴答就取根節點,並且是完全二叉樹,因此比較容易理解,我也懶得畫圖了,截了書上的圖:
從圖中可以看到,對時間堆的關鍵操作其實就是上濾和下濾,上濾也就是簡單的將完全二叉樹待添加的定時器最爲最後一個節點並與父節點比較,如果小於,則交換兩節點位置,再將此節點與父節點比較,直到待插入節點值大於父節點值或者已經是根節點即停止;下濾就是觸發一個定時器之後刪除此定時器,然後取完全二叉樹的深度最大一排的最後一個葉子節點作爲根節點,然後用此根節點與兩個兒子節點比較,若根節點值大於兒子節點中最大者,則交換這兩個節點,再以這個節點做同樣的下濾,如果對於任意完全二叉樹,這樣簡單的下濾不能找出最小節點,但是配合定時器添加時的上濾操作後的完全二叉樹,這樣就可行。
下面貼自己改的代碼:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <time.h>
typedef struct client_data {
char buf[512];
time_t tt;
void* data;
} client_data;
typedef struct heap_timer {
//單個定時器設置絕對時間
time_t expire;
//加的設置的定時器到時秒數
int timeout;
//到時回調函數
void (*cb_func)( void* );
//回調函數參數
struct client_data* user_data;
} heap_timer ;
typedef struct h_t_manager {
//定時器指針數組
struct heap_timer** array;
//當前定時管理器支持的最大定時器個數
int capacity;
//當前定時管理器上的定時器個數
int cur_size;
} h_t_manager ;
void cb_func( void* param )
{
time_t tt = time(NULL);
printf("\n-----------------------------------------------\n");
printf("\tontime,cur time - old time:%d\n", (int)(tt - ((client_data*)param)->tt));
printf("\toldtime:%s", ctime(&(((client_data*)param)->tt)));
printf("\tcurtime:%s", ctime(&tt));
printf("-----------------------------------------------\n");
}
static void percolate_down( h_t_manager* tmanager, int hole )
{
heap_timer* temp = tmanager->array[hole];
int flag = 0;
int child = 0;
for ( ; ((hole*2+1) <= (tmanager->cur_size-1)); hole = child )
{
flag = 0;
child = hole * 2 + 1;
//這裏找出當前節點最小兒子節點
if ( (child < (tmanager->cur_size-1))
&& (tmanager->array[child+1]->expire)
< tmanager->array[child]->expire )
{
++child;
}
//比較待刪除節點和最小兒子節點,若大於就交換
if ( tmanager->array[child]->expire < temp->expire )
{
//這裏的交換其實該用內存拷貝比較好
int tmp_expire = tmanager->array[hole]->expire;
int tmp_timeout = tmanager->array[hole]->timeout;
tmanager->array[hole]->expire = tmanager->array[child]->expire;
tmanager->array[hole]->timeout = tmanager->array[child]->timeout;
tmanager->array[child]->expire = tmp_expire;
tmanager->array[child]->timeout = tmp_timeout;
}
else
{
break;
}
//新的下濾比較
temp = tmanager->array[child];
//tmanager->array[hole] = temp;
}
}
//將堆數組的容量擴大1倍
static int resize( h_t_manager* tmanager )
{
heap_timer** temp = (heap_timer**)
malloc( 2 * tmanager->capacity * sizeof(h_t_manager) );
int i = 0;
for ( ; i < 2 * tmanager->capacity; ++i )
{
temp[i] = NULL;
}
if ( !temp )
{
return -1;
}
tmanager->capacity = 2 * tmanager->capacity;
for ( i = 0; i < tmanager->cur_size; ++i )
{
temp[i] = tmanager->array[i];
free(tmanager->array[i]);
tmanager->array[i] = NULL;
}
tmanager->array = temp;
return 0;
}
int init_ht_manager_0( h_t_manager* tmanager, int cap )
{
tmanager->capacity = cap;
tmanager->cur_size = 0;
tmanager->array = (heap_timer**)malloc(cap * sizeof(h_t_manager));
int i = 0;
for ( ; i < cap; ++i )
{
tmanager->array[i] = NULL;
}
}
//添加定時器
int add_timer( h_t_manager* tmanager, int timeout )
{
if ( !tmanager || timeout <= 0)
{
return -1;
}
if ( tmanager->cur_size >= tmanager->capacity )
{
resize( tmanager );
}
int hole = tmanager->cur_size++;
int parent = 0;
heap_timer* timer = (heap_timer*)malloc(sizeof(heap_timer));
time_t tt = time(NULL);
timer->expire = (int)tt + timeout;
timer->timeout = timeout;
timer->user_data = (client_data*)malloc(sizeof(client_data));
timer->user_data->tt = time(NULL);
timer->cb_func = cb_func;
for ( ; hole > 0; hole = parent )
{
parent = ( hole - 1 ) / 2;
if ( tmanager->array[parent]->expire <= timer->expire )
{
break;
}
tmanager->array[hole] = tmanager->array[parent];
}
tmanager->array[hole] = timer;
return 0;
}
int del_timer( h_t_manager* tmanager, heap_timer* timer )
{
if ( !tmanager || !timer )
{
return -1;
}
timer->cb_func = NULL;
}
int empty( h_t_manager* tmanager )
{
return tmanager->cur_size == 0;
}
heap_timer* top( h_t_manager* tmanager )
{
if ( empty(tmanager) )
{
printf("!!!!!!!top->empty cur size\n");
return NULL;
}
return tmanager->array[0];
}
int pop_timer( h_t_manager* tmanager )
{
if ( empty( tmanager ) )
{
printf("!!!!!!!pop_timer->empty cur size\n");
return -1;
}
if ( tmanager->array[0] )
{
free( tmanager->array[0] );
tmanager->array[0] = NULL;
tmanager->array[0] = tmanager->array[--tmanager->cur_size];
percolate_down( tmanager, 0 );
}
return 0;
}
void tick( h_t_manager* tmanager )
{
heap_timer* tmp = tmanager->array[0];
time_t cur = time( NULL );
while ( !empty(tmanager) )
{
if ( !tmp )
{
break;
}
if ( tmp->expire > cur )
{
break;
}
if ( tmanager->array[0]->cb_func )
{
printf("timer on time,heap:");
int i = 0;
for ( ; i < 10; i++ )
{
if ( tmanager->array[i] )
printf("%d:%d ", i, tmanager->array[i]->timeout);
}
tmanager->array[0]->cb_func( tmanager->array[0]->user_data );
}
pop_timer( tmanager );
tmp = tmanager->array[0];
printf("after timer on time,heap:");
int i = 0;
for ( ; i < 10; i++ )
{
if ( tmanager->array[i] )
printf("%d:%d ", i, tmanager->array[i]->timeout);
}
printf("\n");
if ( tmanager->array[0] )
printf("the next alarm is:%d\n", (int)(tmanager->array[0]->timeout));
printf("current timer count:%d\n", tmanager->cur_size);
}
}
h_t_manager tmanager;
void alarm_handler( int sig )
{
tick( &tmanager );
alarm( tmanager.array[0]->expire - time(NULL) );
}
int main()
{
printf("start timer 。。。。。。。\n");
init_ht_manager_0( &tmanager, 20 );
add_timer( &tmanager, 11 );
add_timer( &tmanager, 5 );
add_timer( &tmanager, 11 );
// add_timer( &tmanager, 22 );
add_timer( &tmanager, 6 );
add_timer( &tmanager, 9 );
// add_timer( &tmanager, 23 );
// add_timer( &tmanager, 33 );
// add_timer( &tmanager, 28 );
// add_timer( &tmanager, 1 );
// add_timer( &tmanager, 66 );
// add_timer( &tmanager, 77 );
// add_timer( &tmanager, 88 );
// add_timer( &tmanager, 55 );
signal( SIGALRM, alarm_handler );
time_t tt = time(NULL);
printf("current time:%s", ctime(&tt));
alarm( tmanager.array[0]->expire - time(NULL) );
while(1)
sleep(5);
return 0;
}
因爲用的完全二叉樹,比較簡單,跟上一篇時間輪一樣,存在注意某些使用問題,整理的時候發現時間堆代碼的註釋還沒寫,想着自己加一點,發現很久前寫的代碼了,有點忘了,加了點也困了,打完羽毛球回來又連擼兩篇博客 ,還被蚊子咬,一撓還撓死個咬得比我寫博客還專注的蚊子 ,醉了 ~