linux网络编程二十二:高性能定时器之时间轮

本文探讨了Linux网络编程中如何通过时间轮解决基于排序链表的定时器效率问题。时间轮利用哈希思想将定时器分散到不同链表,降低插入操作的复杂度,提供高效定时器管理。文章介绍了时间轮的工作原理,包括时间间隔、槽数量与定时精度的关系,并分析了其时间复杂度。同时,提到了多轮时间轮的优化方案,进一步提升执行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前我们提到,基于排序链表的定时器存在一个问题:添加定时器的效率偏低。下面我们讨论的时间轮解决了这个问题。

如图,这是一种简单的时间轮:


轮中的实线指针指向轮子上的一个槽(slot),它以恒定的速度顺时针转动,每转动一步就指向下一个槽,每次转动称为一个滴答(tick)。

一个滴答的时间称为是间轮的槽间隔si(slot interval),它实际上就是心跳时间。

该轮共有N个槽,因此它运转一周的时间是N×si 。每个槽指向一条定时器链表,每条链表上的定时器具有相同的特性:它们的定时时间相差N×si的整数倍。时间轮正是利用这个关系将定时器散列到不同的链表中。

假如现在指针指向槽cs,我们要添加一个定时时间为ti的定时器,则该定时器将被插入ts(timer slot)对应的链表中:ts = (cs + (ti / si)) %N

基于排序链表的定时器使用唯一的一条链表来管理所有定时器,所以插入操作的效率随着定时器数目的增多而降低。而时间轮使用哈希表的思想,将定时器散列到不同的链表上。

这样每条链表上的定时数目都将明显减少,插入操作的效率受定时器数目的影响较少。

很显然,对时间轮而言,要提高定时精度,就要使si值足够小;要提高执行效率,则要求N值足够大。


下面我们实现一个简单的时间轮,只实现一个轮子。复杂的时间轮可以有多个,不同的轮子有不同粒度。相邻的两个轮子,精度高的转一圈,精度低的仅往前移动一槽,就像水表一样。

对时间轮而言,添加一个定时器的时间复杂度是O(1), 删除一个定时器的时间复杂度是O(1),执行一个定时器的时间复杂度是O(n)。

但实际上执行一个定时器的效率要比O(n)高得多,因为时间轮将所有的定时器散列到了不同的链表上,时间轮的槽越多,等于散列表的入口越多,从而每条链表上的定时器数量越少。此外,我们的代码仅用了一个时间轮,当使用多个轮子来实现时,它的时间复杂度将接近O(1)。

关于linux下定时器的实现方式对比,大家可以看看这篇文章,写的挺不错的:http://www.ibm.com/developerworks/cn/linux/l-cn-timers/


1. 代码:

//tw_timer.h
#ifndef __TIME_WHEEL__
#define __TIME_WHEEL__

#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;
};

//定时器
class tw_timer
{
public:
    tw_timer(int rot, int ts)
        :next(NULL), prev(NULL), rotation(rot), time_slot(ts) {}

public:
    int rotation;                       //定时器在时间轮上转多少圈后生效
    int time_slot;                      //定时器属于时间轮上的哪个槽

    void (*cb_func)(client_data*);      //定时器的回调函数
    client_data *user_data;             //客户端数据
    
    tw_timer *prev;                     //指向上一个定时器
    tw_timer *next;                     //指向下一个定时器
};

//时间轮
class time_wheel
{
public:
    time_wheel();
    ~time_wheel();

    tw_timer* add_timer(int timeout);   //根据定时值创建定时器,并插入合适的位置
    void del_timer(tw_timer *timer);    //删除目标定时器
    void tick();                //时间到后调用该函数,时间轮向前滚动一个槽间隔
    
private:
    static const int N = 60;    //时间轮上槽的数目
    static const int TI = 1;    //槽间隔时间,即每1秒时间轮转动一次

    int cur_slot;               //时间轮的当前槽
    tw_timer *slots[N];       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值