ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Golang定时器——Timer 和 Ticker

2022-01-18 13:31:58  阅读:164  来源: 互联网

标签:定时器 Timer Golang 01 timer time Ticker


1、概述

在 Go 里有很多种定时器的使用方法,像常规的 Timer、Ticker 对象,以及经常会看到的 time.After(d Duration) 和 time.Sleep(d Duration) 方法。以上这些定时器的使用方法都来自Golang 原生 time 包,使用time包可以用来执行一些定时任务或者是周期性的任务。

2、定时器使用

2.1 Timer 相关

func NewTimer(d Duration) *Timer
func (t *Timer) Reset(d Duration) bool
func (t *Timer) Stop() bool
func After(d Duration) <-chan Time
func AfterFunc(d Duration, f func()) *Timer

//timer例子
func main() {
   timer := time.NewTimer(3 * time.Second)  //启动定时器,生产一个Timer对象
   select {
   case <-timer.C:
      fmt.Println("3秒执行任务")
   }
   timer.Stop() // 不再使用了,结束它
}

//time.After例子
func main() {
   tChannel := time.After(3 * time.Second) // 其内部其实是生成了一个Timer对象
   select {
   case <-tChannel:
      fmt.Println("3秒执行任务")
   }
}

func main() {
  timer := time.NewTimer(3 * time.Second)
  for {
    timer.Reset(4 * time.Second) // 这样来复用 timer 和修改执行时间
    select {
    case <-timer.C:
      fmt.Println("每隔4秒执行任务")
    }
  }
}

从上面可以看出来 Timer 允许再次被启用,而 time.After 返回的是一个 channel,将不可复用。

而且需要注意的是 time.After 本质上是创建了一个新的 Timer 结构体,只不过暴露出去的是结构体里的 channel 字段而已。

因此如果在 for{...}里循环使用了 time.After,将会不断的创建 Timer。如下的使用方法就会带来性能问题:

错误使用:

for 里的 time.After 将会不断的创建 Timer 对象,虽然最终会回收,但是会造成无意义的cpu资源消耗

func main() {
   for {
      select { 
      case <-time.After(3 * time.Second): 
         fmt.Println("每隔3秒执行一次")
      }
   }
}

正确使用:

func main() {
   timer := time.NewTimer(3 * time.Second) 
   for {
      timer.Reset(3 * time.Second) // 这里复用了 timer
      select {
      case <-timer.C:
         fmt.Println("每隔3秒执行一次")
      }
   }
}

2.2 Ticker 相关

这里的 Ticker 跟 Timer 的不同之处,就在于 Ticker 时间达到后不需要人为调用 Reset 方法,会自动续期。

func NewTicker(d Duration) *Ticker
func Tick(d Duration) <-chan Time
func (t *Ticker) Stop()

func main() {
  ticker := time.NewTicker(3 * time.Second)
  for range ticker.C {
    fmt.Print("每隔3秒执行任务")
  }
  ticker.Stop()
}

错误使用:

func main() {
   for {
      select {
      case <-time.Tick(3 * time.Second): // 这里会不断生成 ticker,而且 ticker 会进行重新调度,造成泄漏
         fmt.Println("每隔3秒执行一次")
      }
   }
} 

3、定时器使用示例

3.1 Ticker定时器

package main
import (
    "fmt"
    "time"
)
func main() {
    // Ticker 包含一个通道字段C,每隔时间段 d 就向该通道发送当时系统时间。
    // 它会调整时间间隔或者丢弃 tick 信息以适应反应慢的接收者。
    // 如果d <= 0会触发panic。关闭该 Ticker 可以释放相关资源。
    ticker1 := time.NewTicker(5 * time.Second)
    // 一定要调用Stop(),回收资源
    defer ticker1.Stop()
    go func(t *time.Ticker) {
        for {
            // 每5秒中从chan t.C 中读取一次
            <-t.C
            fmt.Println("Ticker:", time.Now().Format("2006-01-02 15:04:05"))
        }
    }(ticker1)
    time.Sleep(30 * time.Second)
    fmt.Println("ok")
}  

执行结果:

Ticker: 2022-01-18 13:24:07
Ticker: 2022-01-18 13:24:12
Ticker: 2022-01-18 13:24:17
Ticker: 2022-01-18 13:24:22
Ticker: 2022-01-18 13:24:27
ok

可以看到每次执行的时间间隔都是一样的。

3.2 Timer定时器

package main

import (
    "fmt"
    "time"
)

func main() {

    // NewTimer 创建一个 Timer,它会在最少过去时间段 d 后到期,向其自身的 C 字段发送当时的时间
    timer1 := time.NewTimer(5 * time.Second)

    fmt.Println("开始时间:", time.Now().Format("2006-01-02 15:04:05"))
    go func(t *time.Timer) {
        times := 0
        for {
            <-t.C
            fmt.Println("timer", time.Now().Format("2006-01-02 15:04:05"))

            // 从t.C中获取数据,此时time.Timer定时器结束。如果想再次调用定时器,只能通过调用 Reset() 函数来执行
            // Reset 使 t 重新开始计时,(本方法返回后再)等待时间段 d 过去后到期。
            // 如果调用时 t 还在等待中会返回真;如果 t已经到期或者被停止了会返回假。
            times++
            // 调用 reset 重发数据到chan C
            fmt.Println("调用 reset 重新设置一次timer定时器,并将时间修改为2秒")
            t.Reset(2 * time.Second)
            if times > 3 {
                fmt.Println("调用 stop 停止定时器")
                t.Stop()
            }
        }
    }(timer1)

    time.Sleep(30 * time.Second)
    fmt.Println("结束时间:", time.Now().Format("2006-01-02 15:04:05"))
    fmt.Println("ok")
}

执行结果:

开始时间: 2022-01-18 13:25:43
timer 2022-01-18 13:25:48
调用 reset 重新设置一次timer定时器,并将时间修改为2秒
timer 2022-01-18 13:25:50
调用 reset 重新设置一次timer定时器,并将时间修改为2秒
timer 2022-01-18 13:25:52
调用 reset 重新设置一次timer定时器,并将时间修改为2秒
timer 2022-01-18 13:25:54
调用 reset 重新设置一次timer定时器,并将时间修改为2秒
调用 stop 停止定时器
结束时间: 2022-01-18 13:26:13
ok

可以看到,第一次执行时间为5秒以后。然后通过调用 time.Reset() 方法再次激活定时器,定时时间为2秒,最后通过调用 time.Stop() 把前面的定时器取消掉。

4、总结

  • ticker定时器表示每隔一段时间就执行一次,一般可执行多次。
  • timer定时器表示在一段时间后执行,默认情况下只执行一次,如果想再次执行的话,每次都需要调用 time.Reset() 方法,此时效果类似ticker定时器。同时也可以调用 Stop() 方法取消定时器
  • timer定时器比ticker定时器多一个 Reset() 方法,两者都有 Stop() 方法,表示停止定时器,底层都调用了stopTimer() 函数。
  • 除了上面的定时器外,Go 里的 time.Sleep 也起到了类似一次性使用的定时功能。只不过 time.Sleep 使用了系统调用。而像上面的定时器更多的是靠 Go 的调度行为来实现。

参考:https://juejin.cn/post/6884914839308533774

参考:https://blog.haohtml.com/archives/19859

标签:定时器,Timer,Golang,01,timer,time,Ticker
来源: https://www.cnblogs.com/zhangmingcheng/p/15817344.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有