高性能计时器Timer的设计(时间轮和时间堆两种方式)

时间轮

  前文提到,基于排序链表的定时器存在一个问题:添加定时器的效率偏低。一种简单的时间轮如图所示:

  在这个时间轮中,实线指针指向轮子上的一个槽(slot)。它以恒定的速度顺时针转动,每转动一步就指向下一个槽(slot)。每次转动称为一个滴答(tick)。一个tick时间间隔为时间轮的si(slot interval)。该时间轮共有N个槽,因此它转动一周的时间是N*si.每个槽指向一条定时器链表,每条链表上的定时器具有相同的特征:它们的定时时间相差N*si的整数倍。时间轮正是利用这个关系将定时器散列到不同的链表中。假如现在指针指向槽cs,我们要添加一个定时时间为ti的定时器,则该定时器将被插入槽ts(timer slot)对应的链表中:

              ts=(cs+(ti/si))%N

  基于排序链表的定时器使用唯一的一条链表来管理所有的定时器,所以插入操作的效率随着定时器的数目增多而降低。而时间轮使用了哈希表处理冲突的思想,将定时器散列到不同的链表上。这样每条链表上的定时器数目都将明显少于原来的排序链表上的定时器数目,插入操作的效率基本不受定时器数目的影响。

  很显然,对于时间轮而言,要提高精度,就要使si的值足够小; 要提高执行效率,则要求N值足够大,使定时器尽可能的分布在不同的槽。

  下列代码描述了一个简单的时间轮,如果想继续提高效率,可以实现多个不同精度的轮子,提高查找的效率。

  1 #ifndef TIME_WHEEL_TIMER
  2 #define TIME_WHEEL_TIMER
  3 
  4 #include <time.h>
  5 #include <netinet/in.h>
  6 #include <stdio.h>
  7 
  8 #define BUFFER_SIZE 64
  9 class tw_timer;
 10 struct client_data//绑定socket和定时器
 11 {
 12     sockaddr_in address;
 13     int sockfd;
 14     char buf[ BUFFER_SIZE ];
 15     tw_timer* timer;
 16 };
 17 
 18 class tw_timer//定时器类
 19 {
 20 public:
 21     tw_timer( int rot, int ts ) 
 22     : next( NULL ), prev( NULL ), rotation( rot ), time_slot( ts ){}
 23 
 24 public:
 25     int rotation;//记录该定时器在时间轮转多少圈后生效
 26     int time_slot;//记录定时器属于那个槽
 27     void (*cb_func)( client_data* );//定时器回调函数
 28     client_data* user_data;//用户数据
 29     tw_timer* next;//指向上一个定时器
 30     tw_timer* prev;//指向下一个定时器
 31 };
 32 
 33 class time_wheel//事件轮管理定时器
 34 {
 35 public:
 36     time_wheel() : cur_slot( 0 )
 37     {
 38         for( int i = 0; i < N; ++i )
 39         {
 40             slots[i] = NULL;//每个槽的头节点初始化为空
 41         }
 42     }
 43     ~time_wheel()
 44     {
 45         for( int i = 0; i < N; ++i )
 46         {
 47             tw_timer* tmp = slots[i];
 48             while( tmp )
 49             {
 50                 slots[i] = tmp->next;
 51                 delete tmp;//遍历每个槽销毁new分配在堆中的定时器
 52                 tmp = slots[i];
 53             }
 54         }
 55     }
 56     tw_timer* add_timer( int timeout )//添加新的定时器,插入到合适的槽中
 57     {
 58         if( timeout < 0 )//时间错误
 59         {
 60             return NULL;
 61         }
 62         int ticks = 0;
 63         if( timeout < TI )//小于每个槽的interval,则为1
 64         {
 65             ticks = 1;
 66         }
 67         else
 68         {
 69             ticks = timeout / TI;//相对当前位置的槽数
 70         }
 71         int rotation = ticks / N;//记录多少圈后生效
 72         int ts = ( cur_slot + ( ticks % N ) ) % N;//确定插入槽的位置
 73         tw_timer* timer = new tw_timer( rotation, ts );//根据位置和圈数,插入对应的槽中
 74         if( !slots[ts] )//所在槽头节点为空,直接插入
 75         {
 76             printf( "add timer, rotation is %d, ts is %d, cur_slot is %d\n", rotation, ts, cur_slot );
 77             slots[ts] = timer;
 78         }
 79         else            //头插法
 80         {
 81             timer->next = slots[ts];
 82             slots[ts]->prev = timer;
 83             slots[ts] = timer;
 84         }
 85         return timer;//返回含有时间信息和所在槽位置的定时器
 86     }
 87     void del_timer( tw_timer* timer )//从时间轮上删除定时器
 88     {
 89         if( !timer )
 90         {
 91             return;
 92         }
 93         int ts = timer->time_slot;//找到所在槽
 94         if( timer == slots[ts] )
 95         {
 96             slots[ts] = slots[ts]->next;
 97             if( slots[ts] )
 98             {
 99                 slots[ts]->prev = NULL;
100             }
101             delete timer;
102         }
103         else
104         {
105             timer->prev->next = timer->next;
106             if( timer->next )
107             {
108                 timer->next->prev = timer->prev;
109             }
110             delete timer;
111         }
112     }
113     void tick()
114     {
115         tw_timer* tmp = slots[cur_slot];//取出当前槽的头节点
116         printf( "current slot is %d\n", cur_slot );
117         while( tmp )//遍历
118         {
119             printf( "tick the timer once\n" );
120             if( tmp->rotation > 0 )
121             {
122                 tmp->rotation--;
123                 tmp = tmp->next;
124             }
125             else
126             {
127                 tmp->cb_func( tmp->user_data );//符合条件,调用回调函数
128                 if( tmp == slots[cur_slot] )
129                 {
130                     printf( "delete header in cur_slot\n" );
131                     slots[cur_slot] = tmp->next;
132                     delete tmp;
133                     if( slots[cur_slot] )
134                     {
135                         slots[cur_slot]->prev = NULL;
136                     }
137                     tmp = slots[cur_slot];
138                 }
139                 else
140                 {
141                     tmp->prev->next = tmp->next;
142                     if( tmp->next )
143                     {
144                         tmp->next->prev = tmp->prev;
145                     }
146                     tw_timer* tmp2 = tmp->next;
147                     delete tmp;
148                     tmp = tmp2;
149                 }
150             }
151         }
152         cur_slot = ++cur_slot % N;
153     }
154 
155 private:
156     static const int N = 60;
157     static const int TI = 1; 
158     tw_timer* slots[N];
159     int cur_slot;
160 };
161 
162 #endif

时间堆实现高性能定时器

  设计定时器的另外一种思路是:将所有定时器中超时时间最小定时器的timeout作为心搏的间隔。这样,当tick()被调用时,超时时间最小的定时器必然到期。

  下列代码给出了一个简单的时间堆的实现

  

  1 #ifndef intIME_HEAP
  2 #define intIME_HEAP
  3 
  4 #include <iostream>
  5 #include <netinet/in.h>
  6 #include <time.h>
  7 using std::exception;
  8 
  9 #define BUFFER_SIZE 64
 10 
 11 class heap_timer;
 12 struct client_data
 13 {
 14     sockaddr_in address;
 15     int sockfd;
 16     char buf[ BUFFER_SIZE ];
 17     heap_timer* timer;
 18 };
 19 
 20 class heap_timer
 21 {
 22 public:
 23     heap_timer( int delay )
 24     {
 25         expire = time( NULL ) + delay;
 26     }
 27 
 28 public:
 29    time_t expire;
 30    void (*cb_func)( client_data* );
 31    client_data* user_data;
 32 };
 33 
 34 class time_heap
 35 {
 36 public:
 37     time_heap( int cap ) throw ( std::exception )
 38         : capacity( cap ), cur_size( 0 )
 39     {
 40     array = new heap_timer* [capacity];
 41     if ( ! array )
 42     {
 43             throw std::exception();
 44     }
 45         for( int i = 0; i < capacity; ++i )
 46         {
 47             array[i] = NULL;
 48         }
 49     }
 50     time_heap( heap_timer** init_array, int size, int capacity ) throw ( std::exception )
 51         : cur_size( size ), capacity( capacity )
 52     {
 53         if ( capacity < size )
 54         {
 55             throw std::exception();
 56         }
 57         array = new heap_timer* [capacity];
 58         if ( ! array )
 59         {
 60             throw std::exception();
 61         }
 62         for( int i = 0; i < capacity; ++i )
 63         {
 64             array[i] = NULL;
 65         }
 66         if ( size != 0 )
 67         {
 68             for ( int i =  0; i < size; ++i )
 69             {
 70                 array[ i ] = init_array[ i ];
 71             }
 72             for ( int i = (cur_size-1)/2; i >=0; --i )
 73             {
 74                 percolate_down( i );
 75             }
 76         }
 77     }
 78     ~time_heap()
 79     {
 80         for ( int i =  0; i < cur_size; ++i )
 81         {
 82             delete array[i];
 83         }
 84         delete [] array; 
 85     }
 86 
 87 public:
 88     void add_timer( heap_timer* timer ) throw ( std::exception )
 89     {
 90         if( !timer )
 91         {
 92             return;
 93         }
 94         if( cur_size >= capacity )
 95         {
 96             resize();
 97         }
 98         int hole = cur_size++;
 99         int parent = 0;
100         for( ; hole > 0; hole=parent )
101         {
102             parent = (hole-1)/2;
103             if ( array[parent]->expire <= timer->expire )
104             {
105                 break;
106             }
107             array[hole] = array[parent];
108         }
109         array[hole] = timer;
110     }
111     void del_timer( heap_timer* timer )
112     {
113         if( !timer )
114         {
115             return;
116         }
117         // lazy delelte
118         timer->cb_func = NULL;
119     }
120     heap_timer* top() const
121     {
122         if ( empty() )
123         {
124             return NULL;
125         }
126         return array[0];
127     }
128     void pop_timer()
129     {
130         if( empty() )
131         {
132             return;
133         }
134         if( array[0] )
135         {
136             delete array[0];
137             array[0] = array[--cur_size];
138             percolate_down( 0 );
139         }
140     }
141     void tick()
142     {
143         heap_timer* tmp = array[0];
144         time_t cur = time( NULL );
145         while( !empty() )
146         {
147             if( !tmp )
148             {
149                 break;
150             }
151             if( tmp->expire > cur )
152             {
153                 break;
154             }
155             if( array[0]->cb_func )
156             {
157                 array[0]->cb_func( array[0]->user_data );
158             }
159             pop_timer();
160             tmp = array[0];
161         }
162     }
163     bool empty() const { return cur_size == 0; }
164 
165 private:
166     void percolate_down( int hole )
167     {
168         heap_timer* temp = array[hole];
169         int child = 0;
170         for ( ; ((hole*2+1) <= (cur_size-1)); hole=child )
171         {
172             child = hole*2+1;
173             if ( (child < (cur_size-1)) && (array[child+1]->expire < array[child]->expire ) )
174             {
175                 ++child;
176             }
177             if ( array[child]->expire < temp->expire )
178             {
179                 array[hole] = array[child];
180             }
181             else
182             {
183                 break;
184             }
185         }
186         array[hole] = temp;
187     }
188     void resize() throw ( std::exception )
189     {
190         heap_timer** temp = new heap_timer* [2*capacity];
191         for( int i = 0; i < 2*capacity; ++i )
192         {
193             temp[i] = NULL;
194         }
195         if ( ! temp )
196         {
197             throw std::exception();
198         }
199         capacity = 2*capacity;
200         for ( int i = 0; i < cur_size; ++i )
201         {
202             temp[i] = array[i];
203         }
204         delete [] array;
205         array = temp;
206     }
207 
208 private:
209     heap_timer** array;
210     int capacity;
211     int cur_size;
212 };
213 
214 #endif

参考文献:《Linux高性能服务器编程》——游双

posted @ 2019-05-13 14:08  unique_ptr  阅读(4628)  评论(0编辑  收藏  举报