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 实现 RTP音视频传输示例详解

    Golang 实现 RTP音视频传输示例详解

    这篇文章主要为大家介绍了Golang实现RTP音视频传输的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Go语言中缓冲bufio的原理解读与应用实战

    Go语言中缓冲bufio的原理解读与应用实战

    Go语言标准库中的bufio包提供了带缓冲的I/O操作,它通过封装io.Reader和io.Writer接口,减少频繁的I/O操作,提高读写效率,本文就来详细的介绍一下,感兴趣的可以学习
    2024-10-10
  • 利用rpm打包上线部署golang代码的方法教程

    利用rpm打包上线部署golang代码的方法教程

    RPM是RPM Package Manager(RPM软件包管理器)的缩写,这篇文章主要给大家介绍了关于利用rpm打包上线部署golang代码的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-01-01
  • RoaringBitmap原理及在Go中的使用详解

    RoaringBitmap原理及在Go中的使用详解

    这篇文章主要为大家介绍了RoaringBitmap原理及在Go中的使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • 浅析go逆向符号恢复

    浅析go逆向符号恢复

    这篇文章主要介绍了go逆向符号恢复的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-08-08
  • Golang import本地包和导入问题相关详解

    Golang import本地包和导入问题相关详解

    这篇文章主要介绍了Golang import本地包和导入问题相关详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • golang实现大文件读取的代码示例

    golang实现大文件读取的代码示例

    在实际工作,我们需要读取大数据文件,文件可能上G百G,所以我们不可能一次性的读取到内存,接下来本文给大家介绍了golang实现大文件读取的示例,需要的朋友可以参考下
    2024-04-04
  • Go语言实现本地缓存的策略详解

    Go语言实现本地缓存的策略详解

    今天给大家分享的是Go语言本地缓存的一些内容,主要是结合bigcache和fastcache两个优秀的开源代码库,总结一些设计思路和感悟,文章通过代码示例介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • golang使用sort接口实现排序示例

    golang使用sort接口实现排序示例

    这篇文章主要介绍了golang使用sort接口实现排序的方法,简单分析了sort接口的功能并实例演示了基于sort接口的排序实现方法,需要的朋友可以参考下
    2016-07-07
  • Go net http超时应用场景全面详解

    Go net http超时应用场景全面详解

    HTTP是一个复杂的多阶段协议,因此没有一个一刀切的超时解决方案,在这篇文章中,我将分解您可能需要应用超时的各个阶段,并研究在服务器端和客户端上执行超时的不同方法
    2024-01-01

最新评论