转载请注明出处: http://blog.csdn.net/luotuo44/article/details/38678333
前面的博文已经说到,如果要对多个超时event同时进行监听,就要对这些超时event进行集中管理,能够方便地(时间复杂度小)获取、加入、删除一个event。
在之前的Libevent版本,Libevent使用小根堆管理这些超时event。小根堆的插入和删除时间复杂度都是O(logN)。在2.0.4-alpha版本时,Libevent引入了一个叫common-timeout的东西来管理超时event,要注意的是,它并不是替代小根堆,而是和小根堆配合使用的。事实上,common-timeout的实现要用到小根堆。
Libevent的小根堆和数据结构教科书上的小根堆几乎是一样的。看一下数据结构和Libevent的小根堆源码,很容易就懂的。这样就不多讲了。
本文主要讲一下common-timeout。从common的字面意思和它的实际使用来说,可以把它翻译成“公用超时”。
common-timeout的用途:
要讲解common-timeout,得先说明它的用途。前面说到它和小根堆是配合使用的。小根堆是用在:多个超时event的超时时长是随机的。而common-timeout则是用在: 大量的超时event具有相同的超时时长。其中,超时时长是指event_add参数的第二个参数。 要注意的是,这些大量超时 event 虽然有相同的超时时长,但它们的超时时间是不同的。因为超时时间 = 超时时长+ 调用event_add时间。毫无疑问,如果有相同超时时长的大量超时event都放到小根堆上,那么效率比较低的。虽然小根堆的插入和删除的时间复杂度都是O(logN),但是如果有大量的N,效率也是会下降很多。
common-timeout的原理:
common-timeout的思想是,既然有大量的超时event具有相同的超时时长,那么就它们必定依次激活。如果把它们按照超时时间升序地放到一个队列中(在Libevent中就是这样做的),那么每次只需检查队列的第一个超时event即可。因为其他超时event肯定在第一个超时之后才超时的。
前面说到common-timeout和小根堆是配合使用的。从common-timeout中选出最早超时的那个event,将之插入到小根堆中。然后通过小根堆对这个event进行超时监控。超时后再从common-timeout中选出下一个最早超时的event。具体的超时监控处理过程可以参考《超时event的处理》一文。通过这样处理后,就不用把大量的超时event都插入到小根堆中。
下面看一下Libevent的具体实现吧。
相关结构体:
首先看一下event_base为common-timeout提供了什么成员变量。
//event-internal.h文件
struct event_base {
//因为可以有多个不同时长的超时event组。故得是数组
//因为数组元素是common_timeout_list指针,所以得是二级指针
struct common_timeout_list **common_timeout_queues;
//数组元素个数
int n_common_timeouts;
//已分配的数组元素个数
int n_common_timeouts_allocated;
};
struct common_timeout_list {
//超时event队列。将所有具有相同超时时长的超时event放到一个队列里面
struct event_list events;
struct timeval duration;//超时时长
struct event timeout_event;//具有相同超时时长的超时event代表
struct event_base *base;
};
在实际应用时,可能超时时长为10秒的有1k个超时event,时长为20秒的也有1k个,这就需要一个数组。数组的每一个元素是common_timeout_list结构体指针。每一个common_timeout_list结构体就会处理所有具有相同超时时长的超时event。
common_timeout_list结构体里面有一个event结构体成员,所以并不是从多个具有相同超时时长的超时event