The timer

There are two kinds of timers for Go:

  • One-time Timer: The value of the Timer is timed once, and the operation will stop when the time is over
  • Periodic timer (Ticker) : A timer periodically times the clock and runs forever unless actively stopped

1. One-time Timer

1.1 introduction

A Timer is a kind of single-event Timer, that is, an event is triggered after a specified time, and the event is notified through its own channel. It is called a single event because a Timer is executed only once and then ends, which is the most important difference between a one-time Timer and a periodic one. NewTimer(d Duration) creates a timer. The parameter is the waiting time. An event is triggered immediately after the time arrives

1.2 Application Scenarios

1.2.1 Setting the timeout period

When a coroutine reads from a pipe, if there is no data in the pipe, the coroutine blocks, waiting for data to be written to the pipe. Sometimes we don’t want the coroutine to block permanently, but rather wait for a specified amount of time. If there is no data written to the pipe after this time, the coroutine can be judged to have timed out and move on to other logic.

Examples are as follows

package main

import (
	"fmt"
	"time"
)

func WaitChannel(conn <-chan string) bool{
	timer:=time.NewTimer(1*time.Second)
	select{
	case <-conn:
		timer.Stop()
		return true
	case <-timer.C:
		fmt.Println("WaitChannel timeout!")
		return false}}func main(a){
	str:=make(chan string)
	str1:=make(chan string.1)

	str1<-"abc"

	ok1:=WaitChannel(str1)
	fmt.Println(ok1)

	ok:=WaitChannel(str)
	fmt.Println(ok)

}
Copy the code

1.2.2 Delaying execution of a method

Sometimes we want a method to execute at some point in the future

package main

import (
	"fmt"
	"log"
	"time"
)

func DelayFunction(a){
	timer:=time.NewTimer(5*time.Second)
	select{
	case <-timer.C:
		log.Println("Delay 5s,Start to do sth.")}}func main(a) {
	start:=time.Now()
	DelayFunction()
	cost:=time.Since(start)
	fmt.Println("cost",cost," s")}Copy the code

1.3 Timer External Interface

  1. Creating a Timer

    To create a Timer, use the func NewTimer(d Duration) *Timer method to specify a time. The Timer starts as soon as it is created, no additional startup command is required.

    Creating a Timer means giving a timing task to the system daemon coroutine, which manages all the timers,

    Sends the current time into the Timer’s pipe as an event when the Timer’s time expires.

  2. Stop timer

    After the Timer is created, you can stop the Timer at any time.

    func(t *Timer) Stop() bool

    The return value indicates whether the timer times out

    • True Stops the timer before it expires. Events are not sent later
    • False The timer expires and stops

    In effect, stopping the timer means notifies the system daemon coroutine to remove the timer

  3. Reset timer

    An expired timer or a stopped timer can be reactivated by resetting the timer as follows:

    func (t *Timer) Reset(d Duration) bool

    The action of resetting is essentially to stop the timer first and then start it, and its return value is the return value of the stop timer.

1.4. Simple interface

In addition to the standard interface above, there are some simple methods that can be used in certain situations to reduce code

  1. After()
  2. AfterFunc()

2. Ticker

2.1 introduction

A Ticker is a periodic timer that periodically triggers an event and transmits the event through the pipe provided by the Ticker.

2.2 Application Scenarios

2.2.1 Simple Scheduled Task

Sometimes we want to execute a task on a regular basis, such as logging once per second

package main

import (
	"log"
	"time"
)

func TickerDemo(a){
	ticker:=time.NewTicker(1*time.Second)
	defer ticker.Stop()

	for range ticker.C{
		log.Println("Ticker tick.")}}func main(a){
	TickerDemo()
}
Copy the code

The for range statement continuously fetches events from the pipe, prints a log when it receives the event, and blocks waiting for the event if there is no data in the pipe. Periodic printing is possible because the Ticker periodically writes events to the pipe

2.2.2 Scheduled Aggregation Tasks

Sometimes we want to package some tasks for batch processing, such as the following scenario:

Buses depart according to the following rules

  • Buses leave every five minutes, whether they are full or not
  • Even if the train is already full, it will leave in less than five minutes
package main

import (
	"bytes"
	"fmt"
	"math/rand"
	"time"
)

func TickerLaunch(a){
	ticker:=time.NewTicker(5*time.Minute)
	maxPassenger:=30
	Passengers:=make([]string.0,maxPassenger)

	for{
		passenger:=GetNewPassenger(1)
		ifpassenger! =""{
			Passengers=append(Passengers,passenger)
		}else{
			time.Sleep(1*time.Second)
		}

		fmt.Println(Passengers)

		select {
		case<-ticker.C:
			Passengers=[]string{}

		default:
			if len(Passengers)>=maxPassenger{
				Passengers=[]string{}
			}

		}

		fmt.Println(Passengers)
	}
}



func GetNewPassenger(codeLen int) string{
	rawStr :="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_"
	buf := make([]byte.0, codeLen)
	b := bytes.NewBuffer(buf)
	rand.Seed(time.Now().UnixNano())
	for rawStrLen := len(rawStr); codeLen >0; codeLen-- {
		randNum := rand.Intn(rawStrLen)
		b.WriteByte(rawStr[randNum])
	}
	return b.String()
}


func main(a){
	TickerLaunch()
}
Copy the code

Just look at the logic, don’t try to run the loop

3. runtimeTimer

Both of the above timers create a runtimeTimer underneath, so runtimeTimer optimization is important in each release

  • Before Go 1.10: All runtimetimers are stored in a global heap;
  • Go 1.10 to 1.13: Runtimetimers are split into multiple global heaps, reducing lock wait times for multiple system coroutines
  • Go 1.14+ : runtimeTimer is stored in each processor P, eliminating specialized system coroutines and reducing the time for system coroutine context switching.

4. Precautions

When using a Ticker, if we forget to stop the Ticker at the end of the use, we will result in resource leaks and increased CPU usage

Normally, we would have stopped the Ticker by following the defer statement when we created the Ticker instance

ticker:=time.NewTicker(1*time.Second)
defer ticker.Stop()
Copy the code