高性能定时器 --- 时间堆

时间堆
    时间轮给我们的感觉依旧不够精细,因为它是按照时间间隔来执行定时器的。
时间堆的设计思路是:
    将所有定时器中超时时间最小的定时器的超时值作为心博间隔。一旦心博函数被调用,超时时间最小的定时器必然到期,我们就可以准确的处理它。然后,再从剩余的定时器中找出超时时间最小的那个,作为下一次心博时间,这样,就显得较为精准了。

    这里我们采用最小堆处理这个问题。


#ifndef MIN_HEAP_TIMER
#define MIN_HEAP_TIMER

#include <stdio.h>
#include <netinet/in.h>
#include <time.h>

#define BUF_SIZE 64

struct client_date
{
    struct sockaddr_in     cd_address;
    int             cd_sockfd;
    char             cd_buf[BUFF_SIZE];
    mh_timer*        cd_timer;        //每个连接都拥有的一个定时器
};

class heap_timer
{
public:
    time_t expire;
    client_date *user_data;
    time_heap(int delay)
    {
        expire = time(NULL)+delay;
    }

    void (*cb_func)(client_date*);
};

class time_heap
{
private:
    heap_timer **array;            //一个指针数组
    int capacity;
    int cur_size;

public:
    //第一种构造函数
    //初始化一个给定大小的空堆
    time_heap(int cap):capacity(cap),cur_size(0)
    {
        array = new heap_timer*[cap];
        if(NULL == array)
            exit(1);

        for(int i=0; i<cap; i++)
            array[i] = NULL;
    }
    //第二种构造函数
    //用已有数组来构造堆
    time_heap(heap_timer **init_array, int cap, int size):capacity(cap),cur_size(size)
    {
        if(NULL == init_array || size<cap)
            exit(1);
        array = new heap_timer*[cap];
        if(NULL == array)
            exit(1);

        for(int i=0; i<cap; i++)
            array[i] = NULL; 

        if(size != 0)
        {
            for(int i=0; i<size; i++)
                array[i] = init_array[i];

            for(int i=(cur_size-1)/2; i>=0; i--)        //对整个堆进行调整(调整的对象是拥有子节点的所有节点)
                adjust_down(i);
        }
    }
    //析构函数
    ~time_heap()
    {
        for(int i=0; i<cur_size; i++)
            delete array[i];
        delete[] array;
    }

    //添加目标定时器
    void add_timer(heap_timer *timer)
    {
        if(NULL == timer)
            return;

        if(cur_size >= capacity)
            resize();

        cur_size ++;
        int s = cur_size;
        int parent;

        for(; s>0; s=parent)
        {
            parent = (s-1)/2;
            //因为是添加进来的,之前部分是排好的。所以只要跟parent比比,一直往上不断比就可以了
            if(array[parent]->expire > array[s])
                array[s] = array[parent];
            else
                break;    
        }

        array[s] = timer;
    }

    //删除目标定时器
    void void del_timer(heap_timer *timer)
    {
        if(NULL == timer)
            return;

        //这是书上的方法,简单,但是容易导致膨胀(不过这种数据结构一般不会突然删某个,而是删顶部元素的)
        timer->cb_func = NULL;

        //复杂点的话就是先得知这个节点下标是哪个,然后让最后一个顶替它,对这个节点进行 adjust_down() 操作
    }

    //获取顶部定时器
    heap_timer *top()
    {
        if(!empty())
            return array[0];
        return NULL;
    }

    //删除顶部定时器
    void del_top()
    {
        if(empty())
            return;

        if(array[0])
        {
            delete array[0];
            array[0] = array[--cur_size];
            adjust_down(array[0]);
        }
    }

    //心搏函数
    void tick()
    {
        heap_timer* tmp = array[0];
        time_t cur = time(NULL);

        while(!empty())
        {
            if(!tmp)
                break;

            if(tmp->expire > cur)
                break;

            if(tmp->cb_func)
                tmp->cb_func(tmp->user_data);

            del_top();
            tmp = array[0];
        }
        
    }

    bool empty()
    {
        return cur_size==0;
    }

    //调整函数(向下)
    void adjust_down(int s)
    {
        heap_timer *tmp = array[s];
        int child;

        //for循环的条件是: 至少s的左子节点存在(比如s为0,至少孩子节点1要存在,才需要调整,否则没有子节点是不需要调整的)
        for(; s*2+1<=cur_size-1; )
        {
            child = s*2+1;
            //第一个条件满足说明s的右子节点也存在
            //第二个条件说明s的右子节点小于左子节点,从而代替左子节点和s节点进行比较
            if((child<cur_size-1) && (array[child+1]->expire < array[child]->expire))
                child++;

            //如果s节点果真大于子节点,那么s节点的位置就要由该子节点来代替
            //因为有tmp变量指向原s节点,所以这里可以直接覆盖,且接下来要调整的对象就是原s节点现在所处的环境
            if(tmp->expire > array[child]->expire)
            {
                array[s] = array[child];
                s = child;
            }
            else
                break;
        }
        
        array[s] = tmp;
    }

    //扩大堆数组的容量
    void resize()
    {
        heap_timer *temp = new heap_timer*[2*capacity];
        if(NULL == temp)
            exit(1);

        for(int i=0; i<2*capacity; i++)
            temp[i] = NULL;

        capacity = 2*capacity;
        for(int i=0; i<cur_size; i++)
            temp[i] = array[i];

        delete[] array;
        array = temp;
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值