Go 定时器

简介: Go 定时器

定时器


Go语言的定时器分为两种:

  • 一次性定时器(Timer):定时器值计时一次,计时结束便停止运行
  • 周期性定时器(Ticker):定时器周期性的进行计时,除非主动停止,否则将永远运行


1.一次性定时器(Timer)


1.1 简介


Timer是一种单一事件的定时器,即经过指定的时间后触发一个事件,这个事件通过其本身提供的channel进行通知。 之所以叫单一事件,是因为Timer只执行一次就结束,这也是一次性定时器与周期性定时器最重要的区别。 通过timer.NewTimer(d Duration)可以创建一个Timer,参数即等待时间,时间到来后立刻触发一个事件


1.2 使用场景


1.2.1 设定超时时间


协程从管道读取数据时,如果管道内没有数据那么协程将被阻塞,一直等待管道中有数据写入;有的时候我们不希望 协程被永久阻塞,而是等待一个指定的时间,如果超过这段时间管道内仍没有数据写入,则协程可以判定为超时, 转而去处理其他逻辑。

例子如下


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(){
  str:=make(chan string)
  str1:=make(chan string,1)
  str1<-"abc"
  ok1:=WaitChannel(str1)
  fmt.Println(ok1)
  ok:=WaitChannel(str)
  fmt.Println(ok)
}
复制代码


image.png

1.2.2 延迟执行某个方法


有的时候我们希望某个方法在今后某个时刻执行


package main
import (
  "fmt"
  "log"
  "time"
)
func DelayFunction(){
  timer:=time.NewTimer(5*time.Second)
  select{
  case <-timer.C:
    log.Println("Delay 5s,Start to do sth.")
  }
}
func main() {
  start:=time.Now()
  DelayFunction()
  cost:=time.Since(start)
  fmt.Println("cost",cost," s")
}
复制代码


image.png

1.3 Timer对外接口


  1. 创建定时器
    使用func NewTimer(d Duration) *Timer方法指定一个时间即可创建一个Timer,Timer一经创建便开始计时,不需要额外的启动命令。
    创建Timer意味着把一个计时任务交给系统守护协程,该协程管理着所有的Timer,
    当Timer的时间到达后向Timer的管道中发送当前的时间作为事件。
  2. 停止定时器Timer创建后可以随时停止,停止计时器方法如下:func(t *Timer) Stop() bool返回值代表定时器是否超时
  • true 定时器超时前停止,后续不会再发送事件
  • false 定时器超时后停止
  1. 实际上,停止计时器意味着通知系统守护协程移除该定时器
  2. 重置定时器
    已过期的定时器或者已经停止的定时器可以通过重置动作重新激活,重置方法如下:
    func (t *Timer) Reset(d Duration) bool
    重置的动作实质上是先停止定时器,再启动,其返回值是停止计时器的返回值。


1.4. 简单接口


除了上面的标准接口,还提供了一些简单方法在特定情况下使用可以减少代码

  1. After()
  2. AfterFunc()


2. 周期性定时器(Ticker)


2.1 简介


Ticker是周期性定时器,即周期性的触发一个事件,通过Ticker本身提供的管道将事件传递出去。


2.2 使用场景


2.2.1 简单定时任务


有时我们希望定时执行一个任务,例如每秒记录一次日志


package main
import (
  "log"
  "time"
)
func TickerDemo(){
  ticker:=time.NewTicker(1*time.Second)
  defer ticker.Stop()
  for range ticker.C{
    log.Println("Ticker tick.")
  }
}
func main(){
  TickerDemo()
}


image.png

for range语句会持续性地从管道中获取事件,收到事件后打印一行日志,如果管道中没有数据则会阻塞等待事件。由于Ticker会周期性地向管道写入事件,所以能实现周期性打印


2.2.2 定时聚合任务


有时我们希望把一些任务打包进行批量处理,例如下面的场景:

公交车发车遵循以下规则


  • 公交车每隔5分钟发车,不管是否已经坐满乘客
  • 已经坐满乘客的情况下,不足5分钟也会发车


package main
import (
  "bytes"
  "fmt"
  "math/rand"
  "time"
)
func TickerLaunch(){
  ticker:=time.NewTicker(5*time.Minute)
  maxPassenger:=30
  Passengers:=make([]string,0,maxPassenger)
  for{
    passenger:=GetNewPassenger(1)
    if passenger!=""{
      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(){
  TickerLaunch()
}
复制代码


具体看看逻辑就好,死循环不要轻易尝试运行


3. runtimeTimer


上面的两种计时器都会在底层创建一个runtimeTimer,所以每一个版本中runtimeTimer的优化都十分重要


  • Go 1.10之前:所有的runtimeTimer保存在一个全局的堆中;
  • Go 1.10~1.13: runtimeTimer被拆分到多个全局堆中 ,减少了多个系统协程的锁等待时间
  • Go 1.14+ : runtimeTimer保存在每个处理器P中,消除了专门的系统协程,减少了系统协程上下文切换的时间。


4. 注意事项


当我们使用Ticker的时候,如果忘记在使用结束后及时停止Ticker,就会造成资源泄露CPU使用率不断升高的情况


通常,我们在创建Ticker实例的时候就应该接着defer语句将Ticker停止


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


目录
相关文章
|
6月前
|
Go
Go 定时器:Timer 和 Ticker
本文深入探讨了 Go 语言中的 Timer 和 Ticker 定时器,详细介绍了它们的创建方式、基本用法以及相关的方法等。此外,文章还概括了这两个定时器之间的主要区别,并强调了在使用过程中的注意事项。
150 2
Go 定时器:Timer 和 Ticker
|
6月前
|
缓存 监控 Go
Go语言定时器实战:性能优化与实用技巧
Go语言定时器实战:性能优化与实用技巧
150 0
|
Linux Go API
GO的定时器Timer 和定时任务cron
GO的定时器Timer 和定时任务cron
|
监控 Go
Go 语言一次性定时器使用方式和实现原理
Go 语言一次性定时器使用方式和实现原理
74 0
Go语言,定时器的使用
Timer 是一种单一事件定时器,就是说 Timer 只执行一次就会结束。
151 0
Go语言,定时器的使用
|
缓存 安全 算法
Go基础:channel、定时器、select、锁、sync、atomic
Go基础:channel、定时器、select、锁、sync、atomic
312 0
Go基础:channel、定时器、select、锁、sync、atomic
|
Go 调度
golang 系列:定时器 timer
在 Go 里有很多种定时器的使用方法,像常规的 Timer、Ticker 对象,以及经常会看到的 time.After(d Duration) 和 time.Sleep(d Duration) 方法,今天将会介绍它们的使用方法以及会对它们的底层源码进行分析,以便于在更好的场景中使用定时器。
401 0
|
5天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
23 2
|
3天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
12 2