网络编程定时器三:使用最小堆

前面讨论的定时方案都是以固定频率调用心搏函数tick,并在其中一次检测到期的定时器,然后执行到期定时器上的回调函数。设计定时器的另一中思路是,将所有定器超时时间最小的一个定时器的超时值作为心搏间隔。这样,一旦心搏函数tick执行,超时时间最小的定时器必然到期。我们就可以从剩余定时器中选出超时时间最小的一个,并将这个时间设为下一次心搏间隔。如此反复,就实现了较为精确的定时。

最小堆适合这种解决方案,下面直接给出最小堆方案的代码,并附有测试用例。不过测试用例我仍然只用了alarm来做测试。

#ifndef MIN_HEAP_H
#define MIN_HEAP_H

#include <iostream>
#include <netinet/in.h>
#include <time.h>
#include <assert.h>
#include <string.h>

const int BUFFER_SIZE = 1024;

class heap_timer;
//绑定socket和定时器
struct client_data {
    sockaddr_in addr_;
    int         sockfd_;
    char        buf_[BUFFER_SIZE];
    heap_timer* timer_;
};
//定时器类
class heap_timer {
public:
    heap_timer(int delay) {
        printf("birth time %d, delay: %d\n", time(NULL), delay);
        expire_ = time(NULL) + delay;  //注意,和之前的升序链表以及时间轮不同,这次我们在timer中初始化生效时间
    }   
public:
    void (*timeout_callback_)(client_data*);  //定时器的回调函数
public:
    time_t       expire_;   //定时器生效的绝对时间
    client_data* user_data_;  //用户数据
};

class time_heap {
public:
    time_heap(int cap) throw (std::exception)  //构造函数之一,初始化一个大小为cap的空堆
        : array_(new heap_timer*[cap]), capacity_(cap), cur_size_(0) {
        memset(array_, 0, sizeof(array_));  //初始化指针
    }
    time_heap(heap_timer** init_array, int size, int capacity) throw (std::exception)  //构造函数之二,使用已有数组来初始堆
        : array_(new heap_timer*[capacity]), cur_size_(size), capacity_(capacity) {
        assert(capacity >= size);
        memset(array_, 0, sizeof(array_));
        if(cur_size_ != 0){
            for(int i=0; i<cur_size_; ++i)
                array_[i] = init_array[i];  //初始化堆数组
            for(int i=((cur_size_-1)>>1); i>=0; --i)
                sift_down(i);   //多数组中的(cur_size_-1)/2 ~ 0 个元素执行下滤操作
        }
    }
    ~time_heap() {   //销毁时间堆
        for(int i=0; i<cur_size_; ++i)
            delete array_[i];
        delete []array_;
    }
public:
    //添加目标定时器timer
    void add_timer(heap_timer* timer) throw (std::exception) {
        assert(timer != NULL);

        if(cur_size_ >= capacity_)  //如果当前堆数组容量不够,扩容
            resize();

        //新插入一个元素,当前堆大小加1,hole是新插入元素的位置
        int hole = cur_size_++;   //hole = cur_size_ - 1
        int parent = 0;
        //对从新插入位置到根节点路径上所有节点进行上滤操作
        for(; hole > 0; hole = parent){
            parent = (hole - 1) >> 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值