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

 更新时间:2023年05月16日 17:02:12   作者:人艰不拆_zmc  
在 Go 里有很多种定时器的使用方法,像常规的 Timer、Ticker 对象,本文主要为大家介绍了Timer与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: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"))
 
            // 从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 的调度行为来实现。

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

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

相关文章

  • Golang中使用不定数量空格分割字符串的方法

    Golang中使用不定数量空格分割字符串的方法

    有这样一个使用空格分割字符串的场景,字符串中被分割的子串之间的空格数量不确定,有一个两个或者多个空格,这种场景下,使用最容易想到的strings.Split函数就做不到了,本文接下来就介绍几种行之有效的方法,需要的朋友可以参考下
    2023-07-07
  • 详解Go语言中的逃逸分析

    详解Go语言中的逃逸分析

    逃逸分析是编译器用于决定将变量分配到栈上还是堆上的一种行为,下面小编就来为大家详细讲讲go语言中是如何进行逃逸分析的,需要的小伙伴可以参考下
    2023-09-09
  • Golang实现支持多种类型的set

    Golang实现支持多种类型的set

    在项目开发中,常常会用到set去重,为什么不写一个set呢,而且go现在支持了泛型,所以本文就来用Golang实现一个支持多种类型的set呢
    2023-05-05
  • 如何在Go中使用Casbin进行访问控制

    如何在Go中使用Casbin进行访问控制

    这篇文章主要介绍了如何在Go中使用Casbin进行访问控制,Casbin是一个强大的、高效的开源访问控制框架,其权限管理机制支持多种访问控制模型,Casbin只负责访问控制
    2022-08-08
  • Golang中context库的高级应用

    Golang中context库的高级应用

    context库不仅对于提升代码的效率和性能至关重要,而且还帮助开发者在复杂的系统中保持代码的清晰和可维护性,下面我们就来看看context库的高级应用吧
    2024-01-01
  • 使用Go goroutine实现并发的Clock服务

    使用Go goroutine实现并发的Clock服务

    这篇文章主要为大家详细介绍了如何使用Go goroutine实现并发的Clock服务,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-06-06
  • Golang源码分析之golang/sync之singleflight

    Golang源码分析之golang/sync之singleflight

    golang/sync库拓展了官方自带的sync库,提供了errgroup、semaphore、singleflight及syncmap四个包,本次先分析第一个包errgroup的源代码,下面这篇文章主要给大家介绍了关于Golang源码分析之golang/sync之singleflight的相关资料,需要的朋友可以参考下
    2022-11-11
  • 聊聊Go语言编译github上的项目遇到的坑

    聊聊Go语言编译github上的项目遇到的坑

    这篇文章主要介绍了解决Go语言编译github上的项目遇到的坑,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Golang安装和使用protocol-buffer流程介绍

    Golang安装和使用protocol-buffer流程介绍

    这篇文章主要介绍了Golang安装和使用protocol-buffer过程,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-09-09
  • 使用Golang实现Sm2加解密的代码详解

    使用Golang实现Sm2加解密的代码详解

    本文主要介绍了Go语言实现Sm2加解密的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-03-03

最新评论