Libevent--Timeout

      超时事件,在libevent中或许收到了更多的关照,这里使用了两种数据来处理,第一个就是小根堆,第二个就是不同相对超时时间的队列common_timeout。

1.为什么要使用两种不同的数据结构呢?

        用于超时管理的min_heap,在执行主循环的过程中,它每次都会去检查min_heap的堆顶event是否超时,如果超时的话就进行相应的处理并且从min_heap中移除这个event,然后调整整个堆,直到堆顶event未超时则停止检查。这样每次删除堆顶超时的event时间复杂度只需要O(logn),假设有m个event超时了需要同时处理,需要花费的时间就是O(mlogn),如果有大量相同的相对超时时间,并且超时时间一致,那么小根堆很多时间都是在调整堆,common_timeout这一结构考虑了这一情况,将相对时间相同的超时event按照超时时间升序连接成队列,只将队首的超时时间放入小根堆中,如果此时超时时间发生了,那么只需要取出小根堆中队首这个元素,直接向后遍历即可,就不需要频繁调整小根堆了。

    单纯的小根堆数据结构,就不在这里介绍了,我将超时时间进行讲解。

2.基本数据结构

//event-internal.h
struct common_timeout_list {
        /* List of events currently waiting in the queue. */
        struct event_list events;  //event的双向链表
        /* 'magic' timeval used to indicate the duration of events in this
         * queue. */
        struct timeval duration;  //该common_timeout_list的相对超时时长,events双向链表中的所有event都是相同的超时时长
        /* Event that triggers whenever one of the events in the queue is
         * ready to activate */
        struct event timeout_event;  //“event代表”,最终只有这个event实际插到了min_heap中
        /* The event_base that this timeout list is part of */
        struct event_base *base;  //该common_timeout_list所在的event_base
};

struct event_base
{
    ......
 
    /** An array of common_timeout_list* for all of the common timeout
         * values we know. */
        struct common_timeout_list **common_timeout_queues;   //common_timeout_list *数组,存放不同超时时长的common_timeout_list的指针
        /** The number of entries used in common_timeout_queues */
        int n_common_timeouts;  //common_timeout_queues中实际的元素个数
        /** The total size of common_timeout_queues. */
        int n_common_timeouts_allocated;  //common_timeout_queues的容量
    ......
}

        每个base中都有一个common_timeout_list *的数组common_timeout_queues,它其中每个元素都指向一个common_timeout_list,而每个common_timeout_list会将一个单独的timeout_event插入小根堆里,其超时时长就是队首的超时时长。然后当处理这个event的时候,其回调函数的额外参数args就是这个common_timeout_list头指针,因为队列中是按超时时长递增的,从而只需要遍历即可。

3. 设置timeout队列common_timeout

       对于一个timeval超时结构体来说,它有两个成员,一个数tv_sec用来指明超时时间中的秒数,一个就是tv_usec用来指明超时时间中的微秒数。由于微秒的数值范围只能是0~999999,而tv_usec的变量类型实际上是32位的,能表示的数值范围远远大于999999,因此用低20位足以来表示timeval中的tv_usec,这样一来,tv_usec的高12位就是没有使用的。而libevent中则是通过这高12位来区分一个timeval超时结构体是common_timeout还是普通的timeout。有如下定义:

//event-internal.h 
#define COMMON_TIMEOUT_MICROSECONDS_MASK       0x000fffff   //取低20位掩码 
  
//event.c 
#define MICROSECONDS_MASK       COMMON_TIMEOUT_MICROSECONDS_MASK   //取低20位,即微秒超时时长 
#define COMMON_TIMEOUT_IDX_MASK 0x0ff00000   //20~27bit为该超时时长在common_timeout_queues中的位置 
#define COMMON_TIMEOUT_IDX_SHIFT 20          //微秒最大为999999,因此用低20位存储即可,高12位中前4位标识是否为common_timeout  
#define COMMON_TIMEOUT_MASK     0xf0000000   //取高四位掩码 
#define COMMON_TIMEOUT_MAGIC    0x50000000   //高四位标志是否为common timeout

          对于高12位来说,tv_usec的高4位用来判断一个这个timeval是否是common_timeout,因为用

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值