时间轮实现定时器(哈西表思想)

来源:互联网 发布:银泰淘宝旗舰店 假货 编辑:程序博客网 时间:2024/05/19 15:23

时间轮使用了哈西表的思想,将定时器散列到不同的链表上,这样每条链表上的定时器的数目将随插入定时器的数量增多明显减少,相比较顺序链表只有一条链表来装入所有定时器而言,哈西表的每条链表上的定时器的数量将很少,定时器的插入速度将不受定时器的数量影响.

time_wheel.h

#ifndef _TIME_WHEEL_TIMER_H

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

#define BUFFER_SIZE 64

class tw_timer;

struct client_data
{
    sockaddr_in address;
    int sockfd;
    char buf[BUFFER_SIZE];
    tw_timer *timer;
    void (*cb_func)(client_data*);
};
class tw_timer//时间轮上的结点
{
public:
    tw_timer(int rot,int ts):next(NULL),prev(NULL),rotation(rot),time_slot(ts){}
    
    int rotation;//记录定时器在多少圈后生效
    int time_slot;//记录定时器属于时间轮的哪个槽
    void (*cb_func)(client_data*);//定时器的回调函数
    client_data* user_data;//客户数据
    tw_timer *next;
    tw_timer *prev;
};
class time_wheel
{
public:
    time_wheel():cur_slot(0)
    {
        for(int i=0;i<N;++i)
        {
            slots[i] = NULL;
        }
    }
    ~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* add_timer(int timeout,client_data* client)
    {
        if(timeout < 0)
        {
            return NULL;
        }
        int ticks = 0;
        if(timeout<SI)
        {
            ticks = 1;
        }
        else
        {
            ticks = timeout/SI;
        }
        int rotation = ticks/N;
        int ts = (cur_slot+(ticks%SI))%N;
        tw_timer* timer = new tw_timer(rotation,ts);
        if(!slots[ts])
        {
            printf("add timer,rotation is %d,ts is %d,cur_slot is %d\n",rotation,ts,cur_slot);
            slots[ts] = timer;
            client->timer = timer;
            timer->user_data = client;
            timer->cb_func = client->cb_func;
        }
        else
        {
            timer->next = slots[ts];
            slots[ts]->prev = timer;
            slots[ts] = timer;
        }
        return timer;
    }
    void 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 = NULL;
            }
            delete timer;
        }
        else
        {
            timer->prev->next = timer->next;
            if(timer->next)
            {
                timer->next->prev = timer->prev;
            }
            delete timer;
        }
    }
    void ticks()
    {
        tw_timer* tmp = slots[cur_slot];
        printf("current slot is %d\n",cur_slot);
        while(tmp)
        {
            printf("tick the timer once\n");
            if(tmp->rotation>0)
            {
                tmp->rotation--;
                tmp = tmp->next;
                printf("++++++++++++++");
            }
            else
            {
            printf("*****************\n");
                tmp->cb_func(tmp->user_data);
            printf("*****************\n");
                if(tmp == slots[cur_slot])
                {
                    printf("delete head in cur_slot\n");
                    slots[cur_slot] = tmp->next;
                    delete tmp;
                    if(slots[cur_slot])
                    {
                        slots[cur_slot]->prev = NULL;
                    }
                    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;
    }
int cur_slot;//时间轮当前在哪个槽
private:
static const int N = 10;//时间轮上的槽数
static const int SI = 1;//时间轮的槽间隔,即每隔一秒时间轮转动一次
tw_timer* slots[N];//时间轮的槽数组
};

#endif


ser.cpp: 服务器测试定时器性能

#include"./time_wheel_timer.h"
#include"./utili.h"
#define FD_LIMIT 65535
#define MAX_EVENT_NUMBER 1024
#define TIMESLOT 5
#define N 10

using namespace std;
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)
{
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);
    setnonblocking(fd);
}
void sig_handler(int sig)
{
    int save_errno = errno;
    int msg = sig;
    send(pipefd[1],(char*)&msg,1,0);
    errno = save_errno;
}
void time_handler()
{
    tw.ticks();
    alarm(TIMESLOT);
}
void addsig(int sig)
{
    struct sigaction sa;
    memset(&sa,'\0',sizeof(sa));
    sa.sa_handler = sig_handler;
    sa.sa_flags |=SA_RESTART;
    sigfillset(&sa.sa_mask);
    assert(sigaction(sig,&sa,NULL)!=-1);
}
void cb_func(client_data* user_data)
{
    epoll_ctl(epollfd,EPOLL_CTL_DEL,user_data->sockfd,0);
    assert(user_data);
    close(user_data->sockfd);
    printf("cllose fd %d\n",user_data->sockfd);
}

int main(int argc,char *argv[])
{
    if(argc<=2)
    {
        printf("usage: %s  ip  port_number\n",basename(argv[0]));
        return 1;
    }
    const char *ip = argv[1];
    int port = atoi(argv[2]);

    struct sockaddr_in address;
    bzero(&address,sizeof(address));
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    inet_pton(AF_INET,ip,&address.sin_addr);

    int sock = socket(AF_INET,SOCK_STREAM,0);
    assert(sock>=0);

    int ret = bind(sock,(struct sockaddr*)&address,sizeof(address));
    assert(ret!=-1);

    ret = listen(sock,5);
    assert(ret!=-1);
    
    epoll_event events[MAX_EVENT_NUMBER];
    int epollfd = epoll_create(5);
    assert(epollfd!=-1);
    addfd(epollfd,sock);

    ret = socketpair(PF_UNIX,SOCK_STREAM,0,pipefd);
    if(ret == -1)
    {
        printf("errno = %d\n",errno);

    assert(ret!=-1);
    }
    setnonblocking(pipefd[1]);
    addfd(epollfd,pipefd[0]);

    addsig(SIGTERM);
    addsig(SIGALRM);

    int stop_sever = 0;

    client_data* users = new client_data[MAX_EVENT_NUMBER];
    int timeout = 0;
    alarm(TIMESLOT);

    while(!stop_sever)
    {
        int number = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
        if(number<0)
        {
            if(errno == EINTR)
            {
                continue;
            }
            else
            {
                printf("errno =  %d\n",errno);
                printf("epoll_wait fail\n");
                break;
            }
        }
        int i=0;
        for(;i<number;++i)
        {
            int sockfd = events[i].data.fd;

            if(sockfd == sock)
            {
                struct sockaddr_in client;
                socklen_t len = sizeof(client);
                int connfd = accept(sock,(struct sockaddr*)&client,&len);
                addfd(epollfd,connfd);
                users[connfd].address = client;
                users[connfd].sockfd = connfd;
                users[connfd].cb_func = cb_func;    
                tw.add_timer(20,&users[connfd]);
            }
            else if((sockfd == pipefd[0])&&(events[i].events & EPOLLIN))
            {
                int sig;
                char signals[1024];
                ret = recv(pipefd[0],signals,sizeof(signals),0);
                if(ret == -1)
                {
                    //handle errno
                    continue;
                }
                else if(ret == 0)
                {
                    continue;
                }
                else
                {
                    int i=0;
                    for(;i<ret;++i)
                    {
                        switch(signals[i])
                        {
                            case SIGALRM:
                            {
                                timeout = 1;
                                break;
                            }
                            case SIGTERM:
                            {
                                stop_sever = 1;
                            }
                        }
                    }
                }
            }
            else if(events[i].events & EPOLLIN)
            {
                memset(users[sockfd].buf,'\0',BUFFER_SIZE);
                ret = recv(sockfd,users[sockfd].buf,BUFFER_SIZE-1,0);
                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
                {
                    printf("get %d bytes of client data %s from %d\n",ret,users[sockfd].buf,sockfd);
                    if(timer)
                    {    
                        int rot = 0;
                        int ts = (tw.cur_slot+20%N)%N;
                        if((tw.cur_slot+20%N) >= N)
                        rot = 1;
                        
                        timer->rotation = rot;
                        timer->time_slot = ts;
                        printf("adjust timer once\n");
                    }
                }
            }
            else
            {
                printf("something else happened\n");
            }
        }
        if(timeout)
        {
            time_handler();
            timeout = 0;
        }
    }
    close(sock);
    close(pipefd[0]);
    close(pipefd[1]);
    delete []users;//???
    return 0;
}

客户端用上一篇的时间堆里的客户端,这里就不附上代码了,还有公共头文件

#ifndef _UTILI_H
#define _UTILI_H
//#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<stdio.h>
#include<errno.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<stdlib.h>
#include<sys/epoll.h>
#include<pthread.h>
#include<iostream>
#include<libgen.h>
 #include <signal.h>
using namespace std;


#endif



阅读全文
0 0
原创粉丝点击