Golang中Timer定时器的使用详解

2023-10-11Golang语言

一、定时器

项目经常会有需求:到达某一个特定的时刻就执行我们设定的逻辑,或者周期性的去执行某一个任务。

二、Timer

Timer是一个一次性的时间定时器,在我们设定的某一个时刻将会执行一次。

// The Timer type represents a single event.
// When the Timer expires, the current time will be sent on C,
// unless the Timer was created by AfterFunc.
// A Timer must be created with NewTimer or AfterFunc.
type Timer struct {
  C <-chan Time
  r runtimeTimer
}

其中的C是一个只读的Channel,如果在没有到达我们设定的时间的时候,管道内会没有数据写入,一直会处于阻塞的状态,到达设定的时间就会向管道写入一个系统时间,触发事件。

三、创建Timer

// NewTimer creates a new Timer that will send
// the current time on its channel after at least duration d.
func NewTimer(d Duration) *Timer

使用示例:

func main() {
  timer := time.NewTimer(2 * time.Second)
  <-timer.C
  fmt.Println("hello world")
}

程序会在两秒之后打印"hello world",这两秒会一直阻塞,直到到达我们的超时时间。

四、停止Timer

func (t *Timer) Stop() boo

返回值如果是true:还没有到达超时时间,在超时时间内就停止了timer。

返回值如果是false:执行stop()时,已经到达了超时时间。

func main() {
  timer := time.NewTimer(2 * time.Second)
  fmt.Println(timer.Stop())
}

五、重置Timer

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

对于已经过期或者已经失效的timer,可以通过重置方法使其继续生效。

使用示例:

func main() {
  timer := time.NewTimer(2 * time.Second)
  <-timer.C
  fmt.Println("hello world 1")
  fmt.Println("stop 1 ", timer.Stop())
  timer.Reset(2 * time.Second)
  fmt.Println("stop 2 ", timer.Stop())
}

运行结果如下:

hello world 1
stop 1 false
stop 2 true

<-timer.C 阻塞,一直到超时时间,之后使用stop()就会出现false,timer.Reset重置之后Timer又生效,再使用stop()就会出现true。

1、time.AfterFunc

func AfterFunc(d Duration, f func()) *Timer

传入的参数为超时时间,还有一个具体的函数f,返回一个Timer的指针,作用是在创建出timer之后,在本Goroutine,等待设置的时间之后执行函数f。

func main() {
  timer := time.AfterFunc(time.Second*1, func() {
   fmt.Println("hello world")
  })
  defer timer.Stop()
  time.Sleep(2 * time.Second)
}

2、time.After

func After(d Duration) <-chan Time {
  return NewTimer(d).C
}

根据函数定义,传入时间参数,最后返回的就是新建Timer里面的管道,这个函数相当于实现了Timer。

time.After 一般会配合select一起使用。

func main() {
  ch := make(chan struct{})
  go func() {
   time.Sleep(time.Second * 5)
   ch <- struct{}{}
  }()
  select {
  case <-ch:
   fmt.Println("channel data")
  case <-time.After(time.Second * 1):
   fmt.Println("time after")
  }
}

使用select来监听两个Channel,第一个ch是五秒之后才会有数据,第二个是一秒之后就会有数据,所以会输出"time after"。

六、Ticker

func NewTicker(d Duration) *Ticker

NewTicker返回 一个Ticker对象。

1、Ticker对象定义

// 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
}

Ticker和Timer对象一样,含有一个channel通道,每隔一段指定的时间会往里面发送数据,根据这个消息管道来触发事件,只有关闭Ticker对象才不会继续发送消息。

2、使用示例

func Watch() chan struct{} {
  ticker := time.NewTicker(2 * time.Second)
  ch := make(chan struct{})
  go func(ticker2 *time.Ticker) {
   defer ticker.Stop()
   for {
     select {
     case <-ticker.C:
      fmt.Println("ticker")
     case <-ch:
      fmt.Println("ch")
      return
     }
   }
  }(ticker)
  return ch
}
func main() {
  ch := Watch()
  time.Sleep(6 * time.Second)
  ch <- struct{}{}
  time.Sleep(2 * time.Second)
  close(ch)
}

输出结果:

ticker
ticker
ticker
ch

七、注意

调用ticker.Stop只会停止ticker,但不会关闭ticker.C这个管道,所以我们还需一个一个channel来控制Goroutine的退出。

版权声明:本文为老张的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://www.webppp.com/view/golang_timer.html