基于排序链表的定时器(https://blog.csdn.net/destory27/article/details/81748580)存在一个问题:添加定时器的效率偏低。
如图所示时间轮内,指针指向轮子上的一个槽,它以恒定的速度顺时针旋转,每旋转一步就指向先一个槽.该时间轮共有N个槽,旋转一周的时间是N*Si,每个槽指向一条定时器链表,没条链表上的定时器具有相同的特征:它们的定时时间相差N*Si的整数倍.时间轮正是利用这个关系将定时器散列到不同的链表中。假如现在指针指向槽cs,我们要添加一个定时时间为ti的定时器,则该定时器将被插入槽ts对应的链表: ts = (cs + ti / si) % N.
基于排序链表的定时器使用唯一的一条链表来管理所有的定时器,所有插入操作的效率随着定时器数目的增多而降低.而时间轮使用哈希表思想,将定时器散列到不同的链表上.对于时间轮,要提高定时精度,就要使si值足够小;要提高执行效率,则要求N值足够大.
#ifndef _TIME_WHEEL_TIMER
#define _TIME_WHEEL_TIMER
//时间轮
#include
#include
#include
#define BUFFER_SIZE 64
class tw_timer; //前向声明
//绑定socket和定时器
struct client_data{
struct sockaddr_in address; //addr
int sockfd;
char buf[BUFFER_SIZE];
tw_timer *timer;
};
/********定时器类*********/
class tw_timer{
public:
tw_timer(int rot, int ts)
:rotation(rot),
time_slot(ts)
{}
void (*cb_func)(client_data *); //定时器回调函数
public:
int rotation; //时间轮转多少圈后生效
int time_slot; //属于时间轮上哪个槽
client_data *user_data{nullptr}; //客户数据
tw_timer *next{nullptr}; //指向下一个定时器
tw_timer *prev{nullptr}; //指向前一个定时器
};
class time_wheel{
public:
time_wheel();
~time_wheel();
tw_timer *add_timer(int timeout); //根据定时值timeout创建一个定时器 插入合适的槽中
void del_timer(tw_timer *timer); //删除目标定时器timer
void tick(); //SI时间到后,调用该函数 时间轮前滚动一个槽的间隔
private:
static const int N = 60; //时间轮上槽的数目
static const int SI = 1; //槽间隔1S
tw_timer *slots[N]; //槽
int cur_slot{0}; //当前槽
};
time_wheel::time_wheel()
{
for(int i = 0; i < N; ++i)
slots[i] = nullptr;
}
time_wheel::~time_wheel()
{
for(int i = 0; i < N; ++i)
{
tw_timer *tmp = slots[i];
while(tmp)
{
slots[i] = tmp->next;
delete tmp;
tmp = slots[i];
}
}
}
//创建定时器
tw_timer* time_wheel::add_timer(int timeout)
{
if(timeout < 0)
return nullptr;
int ticks = 0;
//根据插入定时器的超时值计算它将在时间轮多少个滴答后被触发
//并将该滴答数存于ticks中
if(timeout < SI)
ticks = 1;
else
ticks = timeout / SI;
//计算多少圈后被触发
int rotation = ticks / N;
//计算插入哪个槽中
int ts = (cur_slot + (ticks % N)) % N;
//创建定时器 在时间轮转动rotation 圈后触发 且位于第ts个槽上
tw_timer *timer = new tw_timer(rotation, ts);
if(!slots[ts])
{
std::cout << "add timer, rotation is " << rotation << " , ts is " << ts << " cur_slot is " << cur_slot << std::endl;
slots[ts] = timer;
}
else
{
timer->next = slots[ts];
slots[ts]->prev = timer;
slots[ts] = timer;
}
return timer;
}
//删除目标定时器
void time_wheel::del_timer(tw_timer *timer)
{
if(!timer)
return;
int ts = timer->time_slot; //在哪个槽上
if(timer == slots[ts]) //如果槽中第一个结点是要删除结点
{
slots[ts] = slots[ts]->next;
if(slots[ts])
slots[ts]->prev = nullptr;
delete timer;
}
else
{
timer->prev->next = timer->next;
if(timer->next)
timer->next->prev = timer->prev;
timer->next = nullptr;
timer->prev = nullptr;
delete timer;
}
return ;
}
//SI时间到后 调用该函数
void time_wheel::tick()
{
tw_timer *tmp = slots[cur_slot]; //取得当前槽的头结点
std::cout << "Current slot is " << cur_slot << std::endl;
while(tmp)
{
std::cout << "tick the timer once" << std::endl;
if(tmp->rotation > 0)
{
tmp->rotation--;
tmp = tmp->next;
}
else
{
tmp->cb_func(tmp->user_data);
if(tmp == slots[cur_slot])
{
std::cout << "delete handle in cur_slot" << std::endl;
slots[cur_slot] = tmp->next;
delete tmp;
if(slots[cur_slot])
slots[cur_slot]->prev = nullptr;
tmp = slots[cur_slot];
}
else
{
tmp->prev->next = tmp->next;
if(tmp->next)
tmp->next->prev = tmp->prev;
tw_timer *tmp2 = tmp->next;
delete tmp;
tmp = tmp2;
}
}
}
cur_slot = ++cur_slot % N;
return;
}
#endif
服务器测试程序:(epoll)
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "TIME_WHEEL_TIMER.hpp"
#define FD_LIMIT 65535
#define MAX_EVENT_NUMBER 1024
#define TIMESLOT 1
static int pipefd[2];
static time_wheel tw;
static int epollfd = 0;
//设置非阻塞
int setnonblocking(int fd)
{
int old_option = fcntl(fd, F_GETFL);
int new_option = old_option | O_NONBLOCK;
fcntl(fd, F_SETFL, new_option);
return old_option;
}
void addfd(int epollfd, int fd)
{
struct epoll_event event;
event.data.fd = fd;
event.events = EPOLLIN | EPOLLET; //ET模式
epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event); //添加到事件列表
setnonblocking(fd); //设置非阻塞
return ;
}
void sig_handler(int sig)
{
int save_errno = errno;
int msg = sig;
send(pipefd[1], (char*)&msg, 1, 0);
errno = save_errno;
}
//信号处理
void addsig(int sig)
{
struct sigaction sa;
(struct sigaction *)memset(&sa, '\0', sizeof(struct sigaction));
sa.sa_handler = sig_handler;
sa.sa_flags |= SA_RESTART;
sigfillset(&sa.sa_mask);
assert(sigaction(sig, &sa, NULL) != -1);
return ;
}
//
void timer_handler()
{
tw.tick();
alarm(TIMESLOT);
}
//回调函数
void cb_func(client_data *user_data)
{
epoll_ctl(epollfd, EPOLL_CTL_DEL, user_data->sockfd, 0);
assert(user_data);
close(user_data->sockfd);
std::cout << "close fd " << user_data->sockfd << std::endl;
}
int main(int argc, char **argv)
{
if(argc <= 2) //使用IP port
{
std::cout << "usage:" << argv[0] << " ipaddr port" << std::endl;
return -1;
}
struct sockaddr_in address;
bzero(&address, sizeof(struct sockaddr_in));
address.sin_family = AF_INET;
inet_pton(AF_INET, argv[1], &address.sin_addr);
address.sin_port = htons(atoi(argv[2]));
//socket
int listenfd = socket(PF_INET, SOCK_STREAM, 0);
assert(listenfd != -1);
int reuse = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
//bind
int ret = bind(listenfd, (struct sockaddr*)&address, sizeof(struct sockaddr_in));
assert(ret != -1);
//listen
ret = listen(listenfd, 5);
assert(ret != -1);
//事件集合
struct epoll_event events[MAX_EVENT_NUMBER];
//列表套接字
int epollfd = epoll_create(5);
assert(epollfd != -1);
addfd(epollfd, listenfd); //添加到事件列表
//双向通信
ret = socketpair(PF_UNIX, SOCK_STREAM, 0, pipefd);
assert(ret != -1);
setnonblocking(pipefd[1]);
addfd(epollfd, pipefd[0]);
//设置信号处理函数
addsig(SIGALRM);
addsig(SIGTERM);
bool stop_server = false;
client_data *users = new client_data[FD_LIMIT];
bool timeout = false;
alarm(TIMESLOT); //定时
while(!stop_server)
{
int num = epoll_wait(epollfd, events, MAX_EVENT_NUMBER, -1);
if(num < 0 && errno != EINTR)
{
std::cout << "epoll failure" << std::endl;
break;
}
for(int i = 0; i < num; ++i)
{
int sockfd = events[i].data.fd;
if(sockfd == listenfd) //新的连接请求
{
struct sockaddr_in client;
socklen_t len = sizeof(struct sockaddr_in);
int connfd = accept(listenfd, (struct sockaddr*)&client, &len);
assert(connfd != -1);
addfd(epollfd, connfd);
users[connfd].address = client;
users[connfd].sockfd = connfd;
//创建定时器
tw_timer *timer = tw.add_timer(5);
timer->user_data = &users[connfd];
timer->cb_func = cb_func; //回调函数
users[connfd].timer = timer;
break;
}
else if(sockfd == pipefd[0] && events[i].events & EPOLLIN) //处理信号
{
int sig;
char signals[1024];
ret = recv(pipefd[0], signals, sizeof(signals), 0);
assert(ret != -1);
if(ret == 0)
continue;
else
{
for(int i = 0; i < ret; ++i)
switch(signals[i])
{
case SIGALRM:
timeout = true; //有定时任务需要处理
break;
case SIGTERM:
stop_server = true;
}
}
}
else if(events[i].events & EPOLLIN) //处理客户连接上接收的数据
{
memset(users[sockfd].buf, '\0', BUFFER_SIZE);
ret = recv(sockfd, users[sockfd].buf, BUFFER_SIZE - 1, 0);
std::cout << "get " << ret << " bytes os client data " << users[sockfd].buf << " from " << sockfd << std::endl;
tw_timer *timer = users[sockfd].timer;
if(ret < 0) //删除定时器 关闭描述符
{
if(errno != EAGAIN)
{
cb_func(&users[sockfd]);
if(timer)
tw.del_timer(timer);
}
}
else if(ret == 0)
{
cb_func(&users[sockfd]);
if(timer)
tw.del_timer(timer);
}
else
{
//调整定时器 延迟关闭
if(timer)
{
time_t cur = time(NULL);
timer->rotation = 1;
std::cout << "adjust timer once" << std::endl;
}
}
}
else
{
;
}
}
if(timeout)
{
timer_handler();
timeout = false;
}
}
close(listenfd);
close(pipefd[1]);
close(pipefd[0]);
delete []users;
return 0;
}
客户端可使用telnet测试。