Go-源码分析与tick实现定时任务
tick实现定时任务
-
示例代码
package main import ( "fmt" "math/rand" "time" ) // schedule task func tack(ticker <-chan time.Time) { // for 循环中不断执行select for { var flag = 0 select { case <-ticker: flag += rand.Intn(1000) fmt.Printf("**********task<%d>**********\n", flag) default: time.Sleep(1) } } } // fake competitor func competitor() { for { fmt.Printf("competitor running...\n") // goroutine 是抢占式的,必须显式地让出CPU资源去处理其它任务 time.Sleep(time.Millisecond) } } // runner func run() { // for {} time.Sleep(time.Second * 5) } func main() { // Tick 返回一个长度为1的channel ticker := time.Tick(time.Second * 2) go tack(ticker) go competitor() run() }
-
源码分析
// Tick is a convenience wrapper for NewTicker providing access to the ticking // channel only. While Tick is useful for clients that have no need to shut down // the Ticker, be aware that without a way to shut it down the underlying // Ticker cannot be recovered by the garbage collector; it "leaks". // Unlike NewTicker, Tick will return nil if d <= 0. func Tick(d Duration) <-chan Time { if d <= 0 { return nil } return NewTicker(d).C // 👈 看这里,返回一个通道 } // A Ticker holds a channel that delivers `ticks' of a clock // at intervals. type Ticker struct { C <-chan Time // The channel on which the ticks are delivered. r runtimeTimer } // NewTicker returns a new Ticker containing a channel that will send the // time with a period specified by the duration argument. // It adjusts the intervals or drops ticks to make up for slow receivers. // The duration d must be greater than zero; if not, NewTicker will panic. // Stop the ticker to release associated resources. func NewTicker(d Duration) *Ticker { if d <= 0 { panic(errors.New("non-positive interval for NewTicker")) } // Give the channel a 1-element time buffer. // If the client falls behind while reading, we drop ticks // on the floor until the client catches up. 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 } // 👇 定时时间到将会往c中发送一个时间对象,task中从管道接手到值后就可执行响应case下的逻辑 func sendTime(c interface{}, seq uintptr) { // Non-blocking send of time on c. // Used in NewTimer, it cannot block anyway (buffer). // Used in NewTicker, dropping sends on the floor is // the desired behavior when the reader gets behind, // because the sends are periodic. select { case c.(chan Time) <- Now(): default: } }