定时器

前言

网络程序一般需要处理的时间有三类:I/O事件,信号事件,定时事件。在上一章我们学习了通过同一事件源对IO事件和信号事件的统一处理,在这章我们将学习对定时事件的统一处理。一般来说我们要将每个定时事件分别封装为定时器,并通过某种数据结构来将所有的定时器串联起来,常见的高性能定时器有时间轮和时间堆。

定时方法

Linux提供了三种定时方法:

  1. socket选项SO_RECVTIMO和SO_SNDTIMEO
    通过getsockopt分别设置接受数据的超时时间和发送数据的超时时间
  2. SIGALRM信号
  3. IO复用系统调用的超时参数

基于升序链表的定时器

#ifndef LST_TIMER
#define LST_TIMER
#include <time.h>
#define BUFFER_SIZE 64
class timer_node;//前向声明
//用户数据结构
struct client_data
{
    sockaddr_in address;
    int sockfd;
    char buf[BUFFER_SIZE];
    timer_node* timer;
};

//定时器类
class timer_node
{
public:
    timer_node():prev(NULL),next(NULL){}
public:
    time_t expire;//任务超时时间
    void (*cb_func)(client_data*);
    client_data* user_data;
    timer_node* prev;
    timer_node* next;
};

//定时器链表,是一个升序双向链表
class timer_list
{
public:
    timer_list():head(NULL),tail(NULL){};
    //析构函数,链表被删除时删除所有定时器
    ~timer_list()
    {
        timer_node* tmp = head;
        while(tmp)
        {
            head = tmp->next;
            delete tmp;
            tmp = head;
        }
    }
    //将目标定时器添加到目标链表中
    void add_timer(timer_node* timer)
    {
        if(!timer)
            return ;
        if(!head)
        {
            head = tail = timer;
            return ;
        }
        //小于head则直接插入
        if(timer->expire < head->expire)
        {
            timer->next = head;
            head->prev = timer;
            head = timer;
            return ;
        }
        //否则
        add_timer(timer,head);

    }

    //当某个定时器的超时时间发生变化,调整该定时器的位置,只考虑超时时间延长的情况
    void adjust(timer_node* timer)
    {
        if(!timer)
        {
            return ;
        }

        timer_node* tmp = timer->next;
        //若调整的定时器在链表尾部,或者说调整后的大小扔然小于下一元素,则不需要调整
        if(!tmp || timer->expire < tmp->expire)
            return ;
        if(timer == head)
        {
            head = head->next;
            head->prev = NULL;
            timer->next = NULL;
            add_timer(timer,head);
        }
        //如果目标定时器不是头节点
        else
        {
            timer->prev->next = timer->next;
            timer->next->prev = timer->prev;
            add_timer(timer,head);
        }
    }
    //将定时器从timer链表中删除
    void del_timer(timer_node* timer)
    {
        if(!timer)
        {
            return ;
        }
        if((timer == head) && (timer == tail))
        {
            delete timer;
            head = NULL;
            tail = NULL;
            return ;
        }
        if(timer == head)
        {
            head = head->next;
            head->prev = NULL;
            delete timer;
            return ;
        }
        if(timer == tail)
        {
            tail->prev = tail;
            tail->next = NULL;
            delete timer;
            return ;
        }

        timer->prev->next = timer->next;
        timer->next->prev = timer->prev;
        delete timer;

    }
    //SIGALRM信号每次被触发就在其信号处理函数中执行一次tick,以处理链表上到期的任务
    void tick()
    {
        if(!head)
        {
            return ;
        }
        printf("timer tick\n");
        time_t cur = time(NULL);//获取系统当前时间
        timer_node* tmp = head;
        //从头节点依次处理每个定时器,直到遇到一个尚未到期的定时器
        while(tmp)
        {
            if(cur < tmp->expire)
                break;
            //调用回调函数,以执行定时任务
            tmp->cb_func(tmp->user_data);
            //执行完定时器的定时任务之后就将它从链表中删除,重置头节点
            head = tmp->next;
            if(head)
            {
                head->prev = NULL;
            }

            delete tmp;
            tmp = head;

        }

    }


private:
    void add_timer(timer_node* timer,timer_node*lst_head)
    {
        timer_node* prev = lst_head;
        timer_node* tmp = prev->next;
        while(tmp)
        {
            if(timer->expire < tmp->expire)
            {
                prev->next = timer;
                timer->next = tmp;
                tmp->prev = timer;
                timer->prev = prev;
                break;
            }
            prev = tmp;
            tmp = tmp->ne
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值