package timewheel import ( "sync" "sync/atomic" "time" ) //分别用位代表每个轮存在的轮子数(2^6,2^6,...2^8) //-------------------------- //| 6 | 6 | 6 | 6 | 8 | //-------------------------- //根据游戏定时器,将第一轮控制在12位,即40960ms以内。 var GRANULARITY int64 = 10 //定时器的最小单位,每格10ms //var wheelBitSize =[]int{8,6,6,6,6} //32bit定时器 var wheelBitSize =[]int{12,7,6,5,2} //32bit定时器 type wheelInfo struct { slotNum int //轮子slot数量 threshold int64 //轮子最大表示数字范围,如果是第一个轮子2^8,第二个轮子是2^6... } var tWheel *timeWheel //时间实例化对象指针 var chanStartTimer chan *Timer //开始定时器Channel var chanStopTimer chan *Timer //停止定时器Channel const chanTimerLen int = 40960 //Channel var timerPool = sync.Pool{New: func() interface{}{ return &Timer{} }} //构造时间轮对象与相关初始化 func init(){ tWheel = newTimeWheel() chanStartTimer = make(chan *Timer,chanTimerLen) chanStopTimer = make(chan *Timer,chanTimerLen) go timerRunning() } //定时器运行与驱动 func timerRunning(){ t := time.NewTicker(time.Millisecond*10) for { /* if test == true { testTimerRunning() }*/ select{ case startTimer:=<-chanStartTimer: tWheel.addTimer(startTimer) case stopTimer:=<-chanStopTimer: tWheel.delTimer(stopTimer) case <-t.C: tWheel.Tick() } } } /* var test bool = false func testTimerRunning(){ for { select { case startTimer := <-chanStartTimer: tWheel.addTimer(startTimer) case stopTimer := <-chanStopTimer: tWheel.delTimer(stopTimer) default: tWheel.TickOneFrame() } } } */ func NewTimerEx(d time.Duration,c chan *Timer,additionData interface{}) *Timer{ if c == nil { c = make(chan *Timer, 1) } timer := tWheel.newTimer(d.Milliseconds()/GRANULARITY,additionData,c) chanStartTimer<-timer return timer } func NewTimer(d time.Duration) *Timer{ timer := tWheel.newTimer(d.Milliseconds()/GRANULARITY,nil,make(chan *Timer, 1)) chanStartTimer<-timer return timer } //链表结点 type stNode struct { prev *Timer next *Timer } //定时器结构体 type Timer struct { stNode expireTicks int64 //到期滴答数 end int32 //是否已经关闭0表示开启状态,1表示关闭 bClose bool //是否关闭 C chan *Timer //定时器管道 AdditionData interface{} //定时器附加数据 } //停止停时器 func (timer *Timer) Close(){ timer.bClose = true //将关闭标志设为1关闭状态 if atomic.SwapInt32(&timer.end,1) == 0 { chanStopTimer<-timer } } //定时器是否已经停止 func (timer *Timer) IsClose() bool { return timer.bClose } func (timer *Timer) IsEnd() bool{ return atomic.LoadInt32(&timer.end) !=0 } func (timer *Timer) doTimeout(){ if atomic.SwapInt32(&timer.end,1) != 0 { return } timer.prev = nil timer.next = nil select { case timer.C <- timer: } } //每个时间轮上的刻度 type slots struct { timer *Timer //定时器链表头 restTicks int64 //当前刻度走完一圈剩余时间ticks } //时间轮子 type stWheel struct { slots []*slots //刻度切片 slotIndex int //当前指针所在的位置索引 } //获取当前轮的总刻度数 func (stw *stWheel) slotSize() int{ return len(stw.slots) } //添加定时器到slots上 func (s *slots) addTimer(timer *Timer){ timer.next = s.timer timer.prev = s.timer.prev s.timer.prev.next = timer s.timer.prev = timer } //当前slots上是否没有定时器 func (s *slots) isEmpty() bool{ return s.timer == s.timer.next } func (s *slots) makeEmpty() { s.timer.next = s.timer s.timer.prev = s.timer } //时间轮结构体 type timeWheel struct { wheels []*stWheel //所有的轮子的切片 wheelInfos []*wheelInfo //所有轮子的信息,预计算存储 wheelSize int //轮子数 currentTime int64 //当前已经走进的的自然时间 currentTicks int64 //当前已经走进的ticks数 } //构建时间轮对象 func newTimeWheel() *timeWheel{ tWheel := &timeWheel{} tWheel.set(wheelBitSize) tWheel.currentTime = GetNow() return tWheel } //设置n位定时器 func (t *timeWheel) set(wheelBitSize []int){ t.wheelSize = len(wheelBitSize) t.wheelInfos = make([]*wheelInfo,len(wheelBitSize)) t.wheels = make([]*stWheel,len(wheelBitSize)) totalBitSize := 0 for idx,bitSize := range wheelBitSize { totalBitSize += bitSize //1.轮子信息 t.wheelInfos[idx] = &wheelInfo{} t.wheelInfos[idx].slotNum = 1 << bitSize t.wheelInfos[idx].threshold = 1<< totalBitSize //2.make轮子里面的slot t.wheels[idx] = &stWheel{} t.wheels[idx].slots = make([]*slots,t.wheelInfos[idx].slotNum) for slotIdx,_ := range t.wheels[idx].slots { t.wheels[idx].slots[slotIdx] = t.newSlot(t.wheels[idx].slots[slotIdx]) //计算当前slot走完剩余的ticks数,以下turns有个特殊处理 //第一个轮子idx==0时每个slot的刻度代表1,从第二个轮子开始即idx>0根据 //t.wheelInfos[idx-1].threshold获得每个刻度值 //turns代表由于前一轮已经走一圈了,那么当前slotIdex应该加1,即:slotIdx+turns var perSlotTicks int64 = 1 turns := 0 if idx>0 { perSlotTicks = t.wheelInfos[idx-1].threshold turns = 1 } s := ((1 << bitSize) - (slotIdx+turns))*int(perSlotTicks) t.wheels[idx].slots[slotIdx].restTicks = int64(s) } } } //构建一个slot(轮子上的刻度) func (t *timeWheel) newSlot(slot *slots) *slots{ //如果是不存在的slot申请内存 if slot == nil { slot = &slots{} timer := &Timer{} slot.timer = timer } //构建双向循环链表 slot.timer.next = slot.timer slot.timer.prev = slot.timer return slot } //获取当前时间戳ms func GetNow() int64 { return time.Now().UnixNano()/int64(time.Millisecond) } //创建定时器 ticks表示多少个ticks单位到期, additionData定时器附带数据, c到时通知的channel func (t *timeWheel) newTimer(ticks int64,additionData interface{},c chan *Timer) *Timer{ pTimer := timerPool.Get().(*Timer) pTimer.end = 0 pTimer.bClose = false pTimer.C = c pTimer.AdditionData = additionData pTimer.expireTicks = ticks+t.currentTicks return pTimer } func ReleaseTimer(timer *Timer) { timerPool.Put(timer) } //添加定时器 func (t *timeWheel) addTimer(timer *Timer) *Timer { //1.计算到期时间ticks ticks := timer.expireTicks - t.currentTicks if ticks<=0 { timer.doTimeout() return timer } //2.for遍历通过ticks找到适合的轮子插入,从底轮子往高找 var slot *slots for wheelIndex,info := range t.wheelInfos { if ticks < info.threshold { var restTicks int64 var slotTicks int64 var slotIndex int //如果不是第0个轮子 if wheelIndex != 0 { //计算前面所有的轮子剩余ticks数总和(即:当前轮子还有多少个ticks会移动到下一个) restTicks = t.getWheelSum(wheelIndex) //当前轮子每个刻度的ticks数 slotTicks = t.wheelInfos[wheelIndex-1].threshold //计算当前落到哪个slotIndex中 slotIndex = (t.wheels[wheelIndex].slotIndex+int((ticks-restTicks)/slotTicks)) % t.wheelInfos[wheelIndex].slotNum }else{ slotIndex = (t.wheels[wheelIndex].slotIndex + int(ticks))%t.wheelInfos[wheelIndex].slotNum } //取得slot对象指针 slot = t.wheels[wheelIndex].slots[slotIndex] break } } //3.如果都找不到失败 if slot == nil { panic("cannot find slot!") //return nil } //4.添加定时器timer到链表 slot.addTimer(timer) return timer } //删除定时器 func (t *timeWheel) delTimer(timer *Timer) { timer.prev.next = timer.next timer.next.prev = timer.prev ReleaseTimer(timer) } //按照自然时间走动时间差计算loop,并且进行Tick func (t *timeWheel) Tick(){ nowTime := GetNow() loop := (nowTime - t.currentTime)/int64(GRANULARITY) if loop> 0 { t.currentTime = nowTime } for i:=int64(0);i= t.wheels[0].slotSize() { t.wheels[0].slotIndex = 0 t.cascade(1) } } //获得当前轮子的slots func (t *timeWheel) getCurrentSlot(wheelIndex int) *slots{ return t.wheels[wheelIndex].slots[t.wheels[wheelIndex].slotIndex] } //获取当前轮wheelIndex转动所需要的ticks数量 func (t *timeWheel) getWheelSum(wheelIndex int) int64{ var ticks int64 //遍历前面n个轮子 for i:=0;i0的轮子) func (t *timeWheel) cascade(wheelIndex int) { if wheelIndex<1 || wheelIndex>=t.wheelSize { return } //1.取得对应的轮子上的slot wheel := t.wheels[wheelIndex] slot := wheel.slots[wheel.slotIndex] //2.将当前的slot遍历并重新加入 currentTimer := slot.timer.next for ;currentTimer!=slot.timer; { //先保存一个定时器指针,预防链表迭代失效问题 nextTimer:=currentTimer.next //如果到时,直接送到channel if currentTimer.expireTicks<= t.currentTicks { if currentTimer.IsEnd() == false { currentTimer.doTimeout() } }else{//否则重新添加,会加到下一级轮中 t.addTimer(currentTimer) } currentTimer = nextTimer } //3.将当前轮清空 wheel.slots[wheel.slotIndex].makeEmpty() //4.如果当前轮子跳过一轮,需要跳动到下一时间轮 wheel.slotIndex++ if wheel.slotIndex>=wheel.slotSize() { wheel.slotIndex = 0 t.cascade(wheelIndex+1) } }