前言
网络程序一般需要处理的时间有三类:I/O事件,信号事件,定时事件。在上一章我们学习了通过同一事件源对IO事件和信号事件的统一处理,在这章我们将学习对定时事件的统一处理。一般来说我们要将每个定时事件分别封装为定时器,并通过某种数据结构来将所有的定时器串联起来,常见的高性能定时器有时间轮和时间堆。
定时方法
Linux提供了三种定时方法:
- socket选项SO_RECVTIMO和SO_SNDTIMEO
通过getsockopt分别设置接受数据的超时时间和发送数据的超时时间 - SIGALRM信号
- 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