1, Foreword
golang's time.Ticker is generally used to execute tasks as a time cycle.
2, demo
This is an example posted on the official website. Its function is to output the current time every 10s.
package main import ( "fmt" "time" ) func main() { ticker := time.NewTicker(time.Second) defer ticker.Stop() done := make(chan bool) go func() { time.Sleep(10 * time.Second) done <- true }() for { select { case <-done: fmt.Println("Done!") return case t := <-ticker.C: fmt.Println("Current time: ", t) } } }
3, Analysis
To understand the code above, study Go Timer, then it must start from time.Ticker Start with this structure.
//Ticker keeps a channel and passes "tick" to it at regular intervals. type Ticker struct { C <-chan Time // There is a read-only channel of C r runtimeTimer } type runtimeTimer struct { tb uintptr i int when int64 period int64 // Mark whether it is a period and the corresponding time f func(interface{}, uintptr) // Processing function arg interface{} seq uintptr }
When using the timing timer, it is obtained through the NewTimer or AfterFunc function.
func NewTimer(d Duration) *Timer { c := make(chan Time, 1) t := &Timer{ C: c, r: runtimeTimer{ when: when(d), //Indicates that f is called when the time period d is reached f: sendTime, // f refers to a function call, where sendTime refers to sending the current time to Timer.C when the d time arrives arg: c, // Arg means that the parameter arg is passed to f when calling F, and c is used to accept the sending time of sendTime }, } startTimer(&t.r) return t }
The specific implementation logic of the timer is in time.go in runtime, which is implemented through the quadtree heap (the i field in the runtimeTimer structure represents the index in the heap). By constructing a minimum heap, it is ensured that the expired timer can be executed as soon as possible. The timer is executed in a special goroutine: go timerproc()
At this time, let's analyze the above code:
package main import ( "fmt" "time" ) func main() { //NewTicker () returns a new Ticker that contains a channel field and sends the current time to the channel every time period d. //It adjusts the time interval or discards the tick information to accommodate slow recipients. //If d < = 0, it will panic. Close the Ticker to release related resources. ticker := time.NewTicker(time.Second) //Stop() closes a Ticker. After closing, no more tick information will be sent. Stop does not close channel t.C to avoid incorrect read success from that channel defer ticker.Stop() done := make(chan bool) go func() { time.Sleep(10 * time.Second) done <- true }() for { select { case <-done: fmt.Println("Done!") return case t := <-ticker.C: fmt.Println("Current time: ", t) } } }
NewTicker () returns a new Ticker that contains a channel field and sends the current time to the channel every time period D. It adjusts the time interval or discards the tick information to accommodate slow recipients. If d < = 0, it will panic. Close the Ticker to release related resources.
Stop() closes a Ticker. After closing, no more tick information will be sent. Stop does not close channel t.C to avoid incorrect read success from that channel
4, Precautions
- After the ticker is created, there is not a tick immediately. The first tick is after x seconds (you wrote it yourself).
- The timer of Go language is essentially a one-way channel. There is a one-way chan of time.Time type in the time.Timer structure type.
- time.NewTicker regularly triggers the execution of the task. When the next execution comes and the current task has not been completed, it will wait for the current task to execute before executing the next task. Check the go official website documentation and code verification.
- t.Stop() does not stop the Timer here. This is because stop will stop the Timer. After stopping, the Timer will not be sent again, but stop will not close the channel to prevent errors from reading the channel. If you want to stop the Timer, you can only let the go program end automatically.