golang 定时器
time.Aftergolang实现计时器: 1.time.After otherTimeChan = time.After(refreshActiviryInterval * time.Second) case <-otherTimeChan: an.checkAndRefreshAcitivity() otherTimeChan = time.After(refreshActiviryInterval * time.Second) 2.time.NewTimer timer := time.NewTimer(3 * time.Second) <-timer.C 3.time.NewTicker ticker := time.NewTicker(1 * time.Second) select { case <-ticker.C: c.SSEvent("stats",Stats()) } 4.time.AfterFunc time.AfterFunc(5 * time.Minute,func() { fmt.Printf("expired") }) 内部如何实现计时: 其底层结构是 func After(d Duration) <-chan Time { return NewTimer(d).C } //到了指定时间now_t+d调用f(arg,seq) func NewTimer(d Duration) *Timer { c := make(chan Time,1) t := &Timer{ C: c,r: runtimeTimer{ when: when(d),//定时时间 f: sendTime,//触发函数 arg: c,//触发函数参数 },} startTimer(&t.r) return t } //到时间后调用sendTime函数,向Channel传消息 func sendTime(c interface{},seq uintptr) { select { case c.(chan Time) <- Now(): default: } } //到了指定时间now_t+d调用f(arg,seq),并计算下一次的时间 func NewTicker(d Duration) *Ticker { if d <= 0 { panic(errors.New("non-positive interval for NewTicker")) } c := make(chan Time,1) t := &Ticker{ C: c,r: runtimeTimer{ when: when(d),period: int64(d),f: sendTime,arg: c,},} startTimer(&t.r) return t } // 时间d后执行f context包就是借此实现计时 func AfterFunc(d Duration,f func()) *Timer { t := &Timer{ r: runtimeTimer{ when: when(d),f: goFunc,arg: f,} startTimer(&t.r) return t } 底层数据结构(多个计时器时谁先触发):
启动一个单独的goroutine,维护了一个”最小堆”,
// Timerproc runs the time-driven events. // It sleeps until the next event in the timers heap. // If addtimer inserts a new earlier event,it wakes timerproc early. func timerproc() { timers.gp = getg() for { lock(&timers.lock) timers.sleeping = false now := nanotime() delta := int64(-1) for { if len(timers.t) == 0 { delta = -1 break } t := timers.t[0] //获取堆顶的节点,计算最快触发事件的时间 delta = t.when - now if delta > 0 { break } if t.period > 0 { //计算下一次的触发时间,并维护最小堆 t.when += t.period * (1 + -delta/t.period) siftdownTimer(0) } else { // 从最小堆中删除 last := len(timers.t) - 1 if last > 0 { timers.t[0] = timers.t[last] timers.t[0].i = 0 } timers.t[last] = nil timers.t = timers.t[:last] if last > 0 { siftdownTimer(0) } t.i = -1 // mark as removed } f := t.f arg := t.arg seq := t.seq unlock(&timers.lock) if raceenabled { raceacquire(unsafe.Pointer(t)) } //调用触发函数 sendTime f(arg,seq) lock(&timers.lock) } if delta < 0 || faketime > 0 { // 如果最小堆中没有timer timers.rescheduling = true goparkunlock(&timers.lock,"timer goroutine (idle)",traceEvGoBlock,1) continue } // 睡眠delta同时监听timers.waitnote timers.sleeping = true noteclear(&timers.waitnote) unlock(&timers.lock) notetsleepg(&timers.waitnote,delta) } } 可能遇见的问题:
func () { select { case b := <-c: balabala return case <-timer.C: continue } }
Timer.Stop/* Stop prevents the Timer from firing. It returns true if the call stops the timer,false if the timer has already expired or been stopped. Stop does not close the channel 1.定时器已经到期 2.定时器未到期 */ if !t.Stop() { <-t.C } 参考链接 func main() { timer := time.NewTimer(3 * time.Second) go func() { <-timer.C fmt.Println("Timer has expired.") }() timer.Stop() time.Sleep(60 * time.Second) } 执行 Timer.ResetReset主要是重新设置倒计时时间。 在使用Reset函数时主要考虑Timer.C这个channel中是否是空的,将里面冗余的值取出来(如果能确保没值则不考虑),避免出现逻辑错误。文档中对Reset的使用描述:如果明确timer已经失效,并且t.C已经被取空,那么可以直接使用Reset。
// if !t.Stop() { // 利用stop判断是否需要清理channel // <-t.C // } // t.Reset(d) func (t *Timer) Reset(d Duration) bool { if t.r.f == nil { panic("time: Reset called on uninitialized Timer") } w := when(d) active := stopTimer(&t.r) t.r.when = w startTimer(&t.r) return active } // 每隔xxx秒xxx func () { timer := time.NewTimer(timeoutDuration) for { case <-timer.C: xxxxx timer.Reset(timeoutDuration) } } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |