目录
以下源码均基于libevent-2.0.21-stable。
在libevent中,使用min_heap这一数据结构来管理各个event的超时,也就是小顶堆,整个堆是根据各个event的超时时间来构成的,因此堆顶肯定就对应超时时间最小的event,这样就可以按照超时顺序进行处理了。
关于小顶堆的性质,可以先参考堆排序中有关大顶堆的描述。
min_heap类型定义
libevent中的min_heap是一个结构体类型,定义如下:
typedef struct min_heap
{
struct event** p; //小顶堆的首地址
unsigned n, a; //n为event *元素个数,a是event指针链表的长度(以event *p为单位)
} min_heap_t;
这里涉及到了3个成员变量,对于struct event ** p,这是一个二级指针,它指向一个struct event *型的变量,而struct event *的话,就应该很熟悉了,它指向一个事件event。通过p这个二级指针也就可以实现小顶堆对应的数组了,原因在于:在C语言中,p[i]是等价于*(p+i)的,而p作为struct event的二级指针,其指向的元素类型为struct event *型,因此 p+i 实际上就是从首地址开始偏移到第i个struct event *型元素,即p+i在数值上就等于(int)p+i*sizeof(struct event *),即p+i为第i个struct event *型元素的地址,因此*(p+i)就是第i个struct event *型元素了,因此,对于struct event ** p来说,p[i]就表示第i个struct event *元素,偏移量为i*sizeof(struct event *)。这样看来struct event ** p实际上就相当于struct event *p[capacity]指针数组,之所以不使用指针数组而是使用二级指针,是因为C语言中不存在像C++中vector那样长度可变的动态容器,如果定义为数组必须指定数组大小,这是不符合要求的,因此直接使用二级指针,在需要添加元素的时候用malloc来分配一个所需大小的内存空间即可。
而对于另外两个成员变量,我们暂且把p作为一个数组,其中每个元素都是strcut event *类型,那么n就表示这个p数组中当前的元素个数,而a则表示p数组最多能容纳的元素个数。(不得不吐槽一下这里变量的命名。。。)这就像C++中vector的size和capacity的区别,前者表示当前容器中的元素个数,后者表示当前容器最多能容纳的元素个数。
在正式分析函数前,还需要知道的是,每个event中,都定义了一个min_heap_idx用来存储event在这个小顶堆p数组中的索引,虽然堆中的每个元素都是一个event指针,但是建堆的依据是这些event各自设置的超时结构体ev_timeout,这是定时器小顶堆实现依据。如下所示。
struct event {
......
union {
......
int min_heap_idx; //event在堆中的索引
} ev_timeout_pos;
......
struct timeval ev_timeout; //超时时间
......
};
min_heap函数
构造/析构函数及初始化
虽然说C语言中没有构造函数和析构函数,但是min_heap也将这种思想进行了体现在了min_heap_ctor函数和min_heap_dtor函数上,从函数名上看就是constructor和destructor的简写,各自定义如下:
void min_heap_ctor(