设为首页 加入收藏

TOP

使用epoll+时间堆实现高性能定时器(一)
2014-11-23 23:22:56 来源: 作者: 【 】 浏览:27
Tags:使用 epoll +时间 实现 高性能 定时器

在开发Linux网络程序时,通常需要维护多个定时器,如维护客户端心跳时间、检查多个数据包的超时重传等。如果采用Linux的SIGALARM信号实现,则会带来较大的系统开销,且不便于管理。


本文在应用层实现了一个基于时间堆的高性能定时器,同时考虑到定时的粒度问题,由于通过alarm系统调用设置的SIGALARM信号只能以秒为单位触发,因此需要采用其它手段实现更细粒度的定时操作,当然,这里不考虑使用多线程+sleep的实现方法,理由性能太低。


通常的做法还有采用基于升序的时间链表,但升序时间链表的插入操作效率较低,需要遍历链表。因此本实现方案使用最小堆来维护多个定时器,插入O(logn)、删除O(1)、查找O(1)的效率较高。


--------------------------------------分割线 --------------------------------------


相关阅读:


--------------------------------------分割线 --------------------------------------


首先是每个定时器的定义:


class heap_timer
{
public:
heap_timer( int ms_delay )
{
gettimeofday( &expire, NULL );
expire.tv_usec += ms_delay * 1000;
if ( expire.tv_usec > 1000000 )
{
expire.tv_sec += expire.tv_usec / 1000000;
expire.tv_usec %= 1000000;
}
}


public:
struct timeva l expire;
void (*cb_func)( client_data* );
client_data* user_data;
~heap_timer()
{
delete user_data;
}
};


包括一个超时时间expire、超时回调函数cb_func以及一个user_data变量,user_data用于存储与定时器相关的用户数据,用户数据可以根据不同的应用场合进行修改,这里实现的是一个智能博物馆的网关,网关接收来自zigbee协调器的用户数据,并为每个用户维护一段等待时间T,在T到来之前,同一个用户的所有数据都存放到user_data的target_list中,当T到来时,根据target_list列表选择一个适当的target并发送到ip_address,同时删除定时器(有点扯远了=。=)。总之,要实现的功能就是给每个用户维护一个定时器,定时值到来时做一些操作。


class client_data
{
public:
client_data(char *address):target_count(0)
{
strcpy(ip_address,address);
}
private:
char ip_address[32];
target target_list[64];
int target_count;
......
};


以下是时间堆的类定义,包括了一些基本的堆操作:插入、删除、扩容,还包括了定时器溢出时的操作函数tick()


class time_heap
{
public:
time_heap( int cap = 1) throw ( std::exception )
: capacity( cap ), cur_size( 0 )
{
array = new heap_timer* [capacity];
if ( ! array )
{
throw std::exception();
}
for( int i = 0; i < capacity; ++i )
{
array[i] = NULL;
}
}


~time_heap()
{
for ( int i = 0; i < cur_size; ++i )
{
delete array[i];
}
delete [] array;
}


public:
int get_cursize()
{
return cur_size;
}


void add_timer( heap_timer* timer ) throw ( std::exception )
{
if( !timer )
{
return;
}
if( cur_size >= capacity )
{
resize();
}
int hole = cur_size++;
int parent = 0;
for( ; hole > 0; hole=parent )
{
parent = (hole-1)/2;
if ( timercmp( &(array[parent]->expire), &(timer->expire), <= ) )
{
break;
}
array[hole] = array[parent];
}
array[hole] = timer;
}
void del_timer( heap_timer* timer )
{
if( !timer )
{
return;
}
// lazy delelte
timer->cb_func = NULL;
}
int top(struct timeva l &time_top) const
{
if ( empty() )
{
return 0;
}
time_top = array[0]->expire;
return 1;
}
void pop_timer()
{
if( empty() )
{
return;
}
if( array[0] )
{
delete array[0];
array[0] = array[--cur_size];
percolate_down( 0 );
}
}
void tick()
{
heap_timer* tmp = array[0];
struct timeva l cur;
gettimeofday( &cur, NULL );
while( !empty() )
{
if( !tmp )
{
break;
}
if( timercmp( &cur, &(tmp->expire), < ) )
{
break;
}
if( array[0]->cb_func )
{
array[0]->cb_func( array[0]->user_data );
}
pop_timer();
tmp = array[0];
}
}
bool empty() const
{
return cur_size == 0;
}
heap_timer** get_heap_array()
{
return array;
}


pr

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇你可能没听过的 Java 8 中的 10 .. 下一篇Linux通过改进的epoll实现对不同..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: