go实现时间轮(timewheel)算法(简易版)

概念

时间轮算法(Time Wheel Algorithm)是一种常用于处理定时任务的数据结构和算法。它可以高效地管理和执行一系列定时任务,特别是在需要频繁添加、删除和执行任务时表现良好。时间轮算法主要用于实现定时器功能,比如在操作系统、网络通信、任务调度等领域。

基本思想

时间轮算法的基本思想是将时间划分为若干个固定大小的时间片(也称为槽位),并且维护一个环形的时间轮数据结构,每个槽位对应一个时间片。时间轮按照时间递增的方向不断前进,当前时间指针指向当前时间所在的槽位。当时间轮的指针指向某个槽位时,执行该槽位上的定时任务。

代码实现

package timewheel

import (
    "sync"
    "time"
)

// 默认一秒作为时间轮间隔
type timewheel struct {
    slot map[int64]*[]func()
    lock sync.RWMutex
}

// 利用时间戳作为key,相同时间戳进入同一个桶中,过期的桶则进行删除
// todo 想要优化可以把map换成定长list(按长度取模放入list),后跟基于[]func列表实现的优先级队列
/*
结构体如下所示:优先级队列可以搜索一下go怎么实现的,很简单
type funcPrio []func()

type timewheel struct {
    wheel []funcPrio
    size int32
    lock sync.RWMutex
}
*/

func NewTimewheel() *timewheel {
    return &timewheel{slot: map[int64]*[]func(){}, lock: sync.RWMutex{}}
}

func (t *timewheel) AddTask(runTm int64, fn func()) bool {
    t.lock.Lock()
    if runTm < time.Now().Unix() {
       t.lock.Unlock()
       return false
    }
    if list, ok := t.slot[runTm]; ok {
       *list = append(*list, fn)
    } else {
       list := []func(){fn}
       t.slot[runTm] = &list
    }
    t.lock.Unlock()
    return true
}

// 套一层,减少go协程执行造成时间轮之间间隔可能操作一秒,不确定是否可以控制住时间间隔
func (t *timewheel) Run() {
    lasttm := time.Now().Unix()
    for {
       now := time.Now().Unix()
       if now-lasttm == 1 {
          lasttm = now
          go t.run(now)
       }
    }
}

func (t *timewheel) run(tm int64) {
    //for {
    //执行时间间隔可能有微差,但大致应该控制在1s之内,暂时我也不清楚怎么控制
    //time.Sleep(time.Second)
    //tm := time.Now().Unix()
    t.lock.Lock()
    if list, ok := t.slot[tm]; ok {
       for _, fn := range *list {
          go fn()
       }
       //自动清理list
       delete(t.slot, tm)
    }
    t.lock.Unlock()
    //}
}

测试代码

package timewheel

import (
    "math/rand"
    "testing"
    "time"
)

func TestRunTimewheel(t *testing.T) {
    tw := NewTimewheel()
    go tw.Run()
    ls := []int64{}
    for i := 0; i < 10; i++ {
       tm := rand.Int63n(int64(5)) + time.Now().Unix() + 1
       //println("add task:", tm)
       ls = append(ls, tm)
       fg := tw.AddTask(tm, func() {
          println("exec hello ", rand.Intn(20)+i)
       })
       if !fg {
          println("addtask false")
       }
    }
    time.Sleep(time.Second * 10)
}

执行结果

=== RUN   TestRunTimewheel
exec hello  6
exec hello  10
exec hello  13
exec hello  13
exec hello  9
exec hello  6
exec hello  9
exec hello  20
exec hello  11
exec hello  4
--- PASS: TestRunTimewheel (10.00s)
PASS

结论

总体实现比较简单,当然也可以进行抽象化处理,写一个优先级队列,但感觉基本思路都差不,还是简单点来吧,能理解思路就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值