0. Preface
When performing concurrent programming, timing functions are sometimes required, such as monitoring whether a GO program will run for too long, printing logs regularly, etc.
There are two main timers in the GO standard library, one is the Timer timer and the other is the Ticker timer. After the Timer timer is used once, it becomes invalid and Reset() is required to take effect again. The Ticker timer will always take effect, and the two will be introduced separately.
1. Timer timer
First, let’s introduce the implementation principle of GO timer.
In a GO process, all timers are protected by a goroutine running the timerproc() function. It uses the time heap (minimum heap) algorithm to protect all Timers. The underlying data structure is based on the minimum heap of the array. The element at the top of the heap is the closest Timer with the interval timeout. This goroutine will periodically wake up, read the Timer at the top of the heap, execute the corresponding f function or sendtime() function (the two functions will be introduced below), and then remove it from the top of the heap.
Let's take a look at the structure of Timer:
type Timer struct { C <-chan Time // contains filtered or unexported fields }
Only one channel is exposed to the outside world in Timer, and this channel is also the core of the timer. When the timing ends, the Timer will send the value to the channel. When the external environment receives the value of this channel, it means that the timer has timed out. It can be used with select to execute some timeout logic. A Timer can be created by, or by, or by, a Timer.
1.1 () and ()
1.1.1 ()
Check out the following simple application code:
package main import ( "fmt" "time" ) type H struct { t * } func main() { ("main") h:=H{t: (1*)} go () (10*) } func (h *H) timer() { for { select { case <-: ("timer") } } }
We created a timer with the time set to 1S. Then use a select to accept the timer's C. The GO program runs the timer() function and will block until the timeout occurs (the data of C is received), and the timer is printed.
Stop() Stop Timer
func (t *Timer) Stop() bool
Stop() is a method of Timer. Calling the Stop() method will stop the timer, invalidate it, and then trigger a timing event.
In fact, after this method is called, this Timer is removed from the time heap.
Reset() reset Timer
Note that the Timer timer will not run again after timeout once, so you need to call the Reset function to reset. Modify the code in select and add a reset code to the Case:
select { case <-: ("timer") (1*) }
You can see that the timer will be printed continuously, because the timer is reset by using the Reset function.
Notice! The Reset method cannot be called at will, and the official website document specifically emphasizes:
For a Timer created with NewTimer, Reset should be invoked only on stopped or expired timers with drained channels.
The general meaning is that unless the Timer has been stopped or timed out, do not call the Reset method, because, if the Timer has not timed out yet, do not go to Stop it first, but directly reset it, then the old Timer still exists and may still be triggered, resulting in some unexpected things. Therefore, the following code is usually used to safely reset a timer that is unknown (in the above code, when Reset is called, it is always in a timeout state):
if !() { select { case <-: default: } } (1*)
1.1.2 ()
This method is like a minimalist version of Timer. Calling() will directly return a channel. When the timeout, this channel will receive a value, and is simply used as follows:
package main import ( "fmt" "time" ) func main() { ("main") go ticker() (100 * ) } func ticker() { for { select { case <-(1 * ): ("timer") } } }
Note that although this method is simple, there is no Reset method to reset the timer, but it can be used to simulate reset with repeated calls of for and select.
1.1.3 sendtime function
The two creation methods of NewTimer and After will execute a built-in function in the standard library: sendTime to send the current time to the channel after the Timer timed out.
1.2
This method can accept a func type parameter. After the timing is over, this function will be run. Check the following code and guess what result will occur?
package main import ( "fmt" "time" ) func main() { ("main") t := (1*, func() { ("timer") }) go timer(t) (10 * ) } func timer(t *) { select { case <-: ("123") } }
As a result, only the main and timer were printed. This is because this method does not call the sendtime() function mentioned above, that is, it will not send the value to the Timer's Channel, so the select will keep blocking.
f function
I specially used AfterFunc and the above NewTimer and After because of the existence of the f function. The Timer created in this way will execute the function f in a separate goroutine after the timeout is reached, and will not execute the sendtime function.
Note that the f parameter passed in externally is not directly run in timerproc, but starts a new goroutine to execute this method.
2. Ticker timer
The Ticker timer can periodically trigger time events without additional Reset operations.
Its usage method is similar to Timer. By creating Ticker, the simple use is as follows:
package main import ( "fmt" "time" ) func main() { ("main") t:=(1*) go timer(t) (10 * ) } func timer(t *) { for{ select { case <-: ("timer") } } }
This is the article about the principle and detailed explanation of the implementation of timers in Go. For more relevant Go timers, please search for my previous articles or continue browsing the following related articles. I hope you will support me in the future!