开发者

Golang定时器Timer与Ticker的使用详解

开发者 https://www.devze.com 2023-11-20 15:08 出处:网络 作者: 人艰不拆_zmc
目录1、概述2、定时器使用2.1 Timer 相关2.2 Ticker 相关3、定时器使用示例3.1 Ticker定时器3.2 Timer定时器4、总结1、python概述
目录
  • 1、概述
  • 2、定时器使用
    • 2.1 Timer 相关
    • 2.2 Ticker 相关
  • 3、定时器使用示例
    • 3.1 Ticker定时器
    • 3.2 Timer定时器
  • 4、总结

    1、python概述

    在 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 Apythonfter(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) // 其内部其实是python生成了一个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:39:30

    Ticker: 2022-01-18 13:39:35

    Ticker: 2022-01-18 13:39:40

    Ticker: 2022-01-18 13:39:45

    Ticker: 2022-01-18 13:39:50

    ok

    Ticker: 2022-01-18 13:39:55

    可以看到每次执行的时间间隔都是一样的,由于main线程结束导致程序结束。

    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"))
     
                // 从jst.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 的调度行为来实现。

    无论哪种计时器,.C 都是一个 chan Time 类型且容量为 1 的单向 Channel,当有超过 1 个数据的时候便会被阻塞,以此保证不会被触发多次。

    到此这篇关于Golang定时器Timer与Ticker的使用详解的文章就介绍到这了,更多相关Golang定时器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号