1、定时器(time.NewTimer)

Go语言的定时器实质是单向通道,time.Timer结构体类型中有一个 time.Time 类型的单向chan,源码(src/time/time.go)如下

  1. type Timerstruct{
  2. C <-chan Time
  3. r runtimeTimer
  4. }

初始化 Timer 方法为NewTimer 示例

  1. package main
  2. import(
  3. "fmt"
  4. "time"
  5. )
  6. func main(){
  7. t := time.NewTimer(time.Second*2)
  8. defer t.Stop()
  9. for{
  10. <-t.C
  11. fmt.Println("at :"+time.Now().String()+" timer running...")
  12. // 需要重置Reset 使 t 重新开始计时
  13. t.Reset(time.Second*2)
  14. }
  15. }

image.png
image.png
输出 timer running… timer running… timer running… timer running… 这里使用 NewTimer 定时器需要 t.Reset 重置计数时间才能接着执行。如果注释 t.Reset(time.Second * 2) 会导致通道堵塞,报 fatal error: all goroutines are asleep - deadlock! 错误。 同时需要注意 defer t.Stop()在这里并不会停止定时器。这是因为 Stop 会停止 Timer,停止后,Timer不会再被发送,但是Stop不会关闭通道,防止读取通道发生错误。 如果想停止定时器,只能让go程序自动结束。 示例 d

  1. package main
  2. import(
  3. "fmt"
  4. "time"
  5. )
  6. func main(){
  7. t := time.NewTimer(time.Second*2)
  8. ch := make(chan bool)
  9. go func(t *time.Timer){
  10. defer t.Stop()
  11. for{
  12. select{
  13. case<-t.C:
  14. fmt.Println("timer running....")
  15. // 需要重置Reset 使 t 重新开始计时
  16. t.Reset(time.Second*2)
  17. case stop :=<-ch:
  18. if stop {
  19. fmt.Println("timer Stop")
  20. return
  21. }
  22. }
  23. }
  24. }(t)
  25. time.Sleep(10* time.Second)
  26. ch <-true
  27. close(ch)
  28. time.Sleep(1* time.Second)
  29. }

image.png

2、定时期(NewTicker)

  1. package main
  2. import(
  3. "fmt"
  4. "time"
  5. )
  6. func main(){
  7. t := time.NewTicker(time.Second*2)
  8. defer t.Stop()
  9. for{
  10. <- t.C
  11. fmt.Println("Ticker running...")
  12. }
  13. }

image.png
结果 Ticker running… Ticker running… Ticker running… ticker只要定义完成后,不需要其他操作就可以定时执行。 这里的defer t.Stop()和上面示例相似,也不会停止定时器,解决办法一样。

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func main() {
  7. ticker := time.NewTicker(2 * time.Second)
  8. ch := make(chan bool)
  9. go func(ticker *time.Ticker) {
  10. defer ticker.Stop()
  11. for {
  12. select {
  13. case <-ticker.C:
  14. fmt.Println("Ticker running...")
  15. case stop := <-ch:
  16. if stop {
  17. fmt.Println("Ticker Stop")
  18. return
  19. }
  20. }
  21. }
  22. }(ticker)
  23. time.Sleep(10 * time.Second)
  24. ch <- true
  25. close(ch)
  26. }

image.png

3、time.After

time.After()表示多长时间长的时候后返回一条 time.Time 类型的通道消息。但是在取出channel内容之前不阻塞,后续程序可以继续执行。

先看源码(src/time/sleep.go)

  1. func After(d Duration)<-chan Time{
  2. returnNewTimer(d).C
  3. }

通过源码我们发现它返回的是一个NewTimer(d).C,其底层是用NewTimer实现的,所以如果考虑到效率低,可以直接自己调用NewTimer。
示例1

  1. package main
  2. import(
  3. "fmt"
  4. "time"
  5. )
  6. func main(){
  7. t := time.After(time.Second*3)
  8. fmt.Printf("t type=%T\n", t)
  9. //阻塞3秒
  10. fmt.Println("t=",<-t)
  11. }

image.png
运行结果

  1. t type=<-chan time.Time t= 2019-05-23 09:58:59.5103274 +0800 CST m=+3.008172101

先打印第一行,3s后打印第二行
基于time.After()特性可以配合select实现计时器
示例2

  1. package main
  2. import(
  3. "fmt"
  4. "time"
  5. )
  6. func main(){
  7. ch1 := make(chan int,1)
  8. ch1 <-1
  9. for{
  10. select{
  11. case e1 :=<-ch1:
  12. //如果ch1通道成功读取数据,则执行该case处理语句
  13. fmt.Printf("1th case is selected. e1=%v\n", e1)
  14. case<-time.After(time.Second*2):
  15. fmt.Println("Timed out")
  16. }
  17. }
  18. }

image.png
1th case is selected. e1=1 Timed out Timed out Timed out Timed out
select 语句阻塞等待最先返回数据的 channel`,如ch1通道成功读取数据,则先输出1th case is selected. e1=1,之后每隔2s输出 Timed out。

当前内容版权归 guyan0319 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请点击 guyan0319 .

原文链接

https://www.bookstack.cn/read/golang_development_notes/zh-2.12.md