golang定时器之timer+ticker
转载: https://juejin.cn/post/7327157426298011663
Timer 是一个一次性的定时器,用于在未来的某一时刻执行一次操作。
基本使用
创建 Timer 定时器的方式有两种:
NewTimer(d Duration) *Timer:该函数接受一个time.Duration类型的参数d(时间间隔),表示定时器在过期之前等待的时间。NewTimer返回一个新的Timer定时器,这个定时器在其内部维护一个通道C,该通道在定时器被触发时会接收当前的时间值。AfterFunc(d Duration, f func()) *Timer:接受一个指定的时间间隔d和回调函数f。该函数返回一个新的Timer定时器,在定时器到期时直接调用f,而不是通过通道C发送信号。调用Timer的Stop方法可以停止定时器和取消调用f。
下面的代码展示了如何使用 NewTimer 和 AfterFunc 来创建定时器以及定时器的基本用法:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/timer/timer/usage.go
package main
import (
"fmt"
"time"
)
func main() {
// 使用 NewTimer 创建一个定时器
timer := time.NewTimer(time.Second)
go func() {
select {
case <-timer.C:
fmt.Println("timer 定时器触发啦!")
}
}()
// 使用 AfterFunc 创建另一个定时器
time.AfterFunc(time.Second, func() {
fmt.Println("timer2 定时器触发啦!")
})
// 主goroutine等待两秒,确保看到定时器触发的输出
time.Sleep(time.Second * 2)
}
代码运行结果如下所示:
timer 定时器触发啦!
timer2 定时器触发啦!
下面是代码的逐步解析:
- 首先使用
NewTimer创建了一个定时器,然后在一个新的goroutine中监听它的C属性以等待定时器触发。 - 其次,使用
AfterFunc创建另一个定时器,通过指定一个 回调函数 来处理定时器到期事件。 - 最后,主
goroutine等待足够长的时间以确保定时器的触发信息能够被打印出来。
方法详解
Reset
Reset(d Duration) bool:该方法用于重置 Timer 定时器的过期时间,也可以理解为重新激活定时器。它接受一个 time.Duration 类型的参数 d,表示定时器在过期之前等待的时间。
除此之外,该方法还返回一个 bool 值:
- 如果定时器处于活动的状态,返回
true。 - 如果定时器已经过期或被停止了,返回
false(false并不意味着激活定时器失败,只是标识定时器的当前状态)。
下面是代码示例:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/timer/timer/reset.go
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(5 * time.Second)
// 第一次重置,定时器处于激活状态,因此返回 true
b := timer.Reset(1 * time.Second)
fmt.Println(b) // true
second := time.Now().Second()
select {
case t := <-timer.C:
fmt.Println(t.Second() - second) // 1s
}
// 第二次重置,定时器已经处于过期状态,因此返回 false
b = timer.Reset(2 * time.Second)
fmt.Println(b) // false
second = time.Now().Second()
select {
case t := <-timer.C:
fmt.Println(t.Second() - second) // 2s
}
}
代码运行结果如下所示:
true
1
false
2
下面是代码的逐步解析:
- 首先,创建了一个定时器,设置为 5 秒后到期。
- 然后调用
Reset方法立即将其重置为 1 秒后到期。因为此时定时器仍处于激活状态(即还未到期),所以Reset方法返回true。 - 接下来的
select语句等待定时器到期,并打印出实际经过的秒数(约等于 1 秒)。 - 接着第二次重置定时器,这次设置为 2 秒后到期。由于定时器在这次重置时已经到期,
Reset方法返回false。 - 最后,再次使用
select语句等待定时器到期,并打印出这次经过的秒数(约等于 2 秒)。
Stop
Stop() bool:该方法用于停止定时器。如果定时器停止成功,返回 true,如果定时器已经过期或被停止,则返回 false。切记:Stop 操作不会关闭通道 C。
下面是代码示例:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/timer/timer/stop.go
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(3 * time.Second)
// 停止定时器,在定时器触发之前停止它,因此返回 true
stop := timer.Stop()
fmt.Println(stop) // true
stop = timer.Stop()
// 第二次停止定时器,此时定时器已经被停止了,返回 false
fmt.Println(stop) // false
}
代码运行结果如下所示:
true
false
下面是代码的逐步解析:
- 首先,创建了一个设置为 3 秒后触发的定时器。
- 然后立即调用
Stop方法停止定时器。因为此时定时器还未触发,所以Stop返回true。 - 最后再次调用
Stop方法尝试停止同一个定时器。由于定时器已经被停止,这次Stop返回false。
Ticker:周期性定时器
Tciker 是一个周期性的定时器,用于在固定的时间间隔重复执行任务。它在每个间隔时间到来时,向其通道(Channel)发送当前时间。
基本使用
我们可以使用 NewTicker 函数来创建一个新的 Ticker 对象,该函数接受一个 time.Duration 类型的参数 d(时间间隔)。
下面是代码示例:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/timer/ticker/usage.go
package main
import (
"context"
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
timeout, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)
defer cancelFunc()
go func() {
for {
select {
case <-timeout.Done():
fmt.Println("timeout done")
return
case <-ticker.C:
fmt.Println("定时器触发啦!")
}
}
}()
// 主goroutine等待 7 秒,确保看到定时器触发的输出
time.Sleep(time.Second * 7)
}
代码运行结果如下所示:
定时器触发啦!
定时器触发啦!
定时器触发啦!
定时器触发啦!
定时器触发啦!
timeout done
下面是代码的逐步解析:
- 首先,创建了一个每秒触发的定时器,确保函数周期结束后清理定时器,我们应该加上
defer ticker.Stop() - 然后,创建一个在 5 秒后超时的上下文。
cancelFunc被用于在退出前清理上下文。 - 接着,在一个新的
goroutine中,select语句用于监听两个通道:定时器的通道 (ticker.C) 和超时上下文的完成通道 (timeout.Done())。当定时器每秒触发时,会打印出消息。当上下文超时(即 5 秒过后),打印出超时信息,并返回从而结束该goroutine。 - 最后,主
goroutine通过time.Sleep(time.Second * 7)等待 7 秒,以确保能够观察到定时器触发和超时事件的输出。
除了使用 select 语句监听 ticker.C 以外,我们还可以使用 for range 的形式进行监听:
for range ticker.C {}
需要注意的是,即使通过 Stop 方法停止 Ticker 定时器,其 C 通道不会被关闭。这意味着无论是通过 for select 还是 for range 去监听 ticker.C,我们需要使用其他机制来退出循环,例如使用 context 上下文。
方法详解
Reset
Reset(d Duration) 方法用于停止计时器并将其周期重置为指定的时间。下一个时间刻度将在新周期结束后生效。它接受一个 time.Duration 类型的参数 d,表示新的周期。该参数必须大于零;否则 Reset 方法内部将会 panic。
下面是代码示例:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/timer/ticker/reset.go
package main
import (
"time"
)
func main() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
// 重置定时器
ticker.Reset(1 * time.Second)
second := time.Now().Second()
for t := range ticker.C {
// 1s
fmt.Printf("周期:%d 秒", t.Second()-second)
break
}
}
代码运行结果如下所示:
周期:1 秒
下面是代码的逐步解析:
- 首先,创建了一个每 5 秒触发一次的定时器
time.Ticker。 - 其次,使用
Reset方法重置定时器的触发间隔。5 秒变成 1 秒。 - 最后通过一次循环,打印定时器的周期,预期结果为 1 秒。
Stop
Stop() 方法用于停止定时器。在 Stop 之后,将不再发送更多的 tick 给其通道 C。切记:Stop 操作不会关闭通道 C。
下面是代码示例:
// https://github.com/chenmingyong0423/blog/blob/master/tutorial-code/go/timer/ticker/stop.go
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(time.Second)
quit := make(chan struct{}) // 创建一个退出通道
go func() {
for {
select {
case <-ticker.C:
fmt.Println("定时器触发啦!")
case <-quit:
fmt.Println("协程停止啦!")
return // 接收到退出信号,退出循环
}
}
}()
time.Sleep(time.Second * 3)
ticker.Stop() // 停止定时器
close(quit) // 发送退出信号
fmt.Println("定时器停止啦!")
}
代码运行结果如下所示:
定时器触发啦!
定时器触发啦!
定时器触发啦!
协程停止啦!
定时器停止啦!
- 首先,创建一个每秒触发一次的
time.Ticker对象。同时,引入了一个类型为chan struct{}的退出通道 quit。这个通道将用于向运行中的goroutine发送停止信号。 - 其次,启动一个新的
goroutine。在这个goroutine中,使用for-select循环来监听两个事件:定时器的触发(case <-ticker.C)和退出信号(case <-quit)。每当定时器触发时,它会打印一条消息。如果收到退出信号,它会打印一条消息并退出循环。 - 接着,在主
goroutine中,time.Sleep(time.Second * 3)模拟了一段等待时间(3 秒),在这期间定时器会触发几次。 - 最后,主
goroutine通过调用Stop方法停止定时器,然后关闭退出通道。goroutine接收到退出信号后打印出一条消息并退出循环。
Stop 不会关闭其通道 C,因此我们需要借助其他方式(例如退出信号)来清理资源。
Timer 和 Ticker 的主要区别
用途:
Timer用于单次延迟执行任务。Ticker重复执行任务。
行为特点:
Timer在设定的延迟时间过后触发一次,发送一个时间值到其通道。Ticker按照设定的间隔周期性地触发,反复发送时间值到其通道。
可控性:
Timer可以被重置(Reset方法)和停止(Stop方法)。Reset用于改变Timer的触发时间。Ticker可以被重置(Reset方法)和停止(Stop方法)。Reset用于改变Ticker触发的时间间隔。
结束操作:
Timer的Stop方法用于阻止Timer触发,如果Timer已经触发,Stop不会从其通道中删除已发送的时间值。Ticker的Stop方法用于停止Ticker的周期性触发,一旦停止,它不会再向通道发送新的值。
注意事项
- 无论是
Timer还是Ticker定时器,调用Stop方法之后,并不会关闭它们的C通道。如果有其他的goroutine在监听这个通道,为避免潜在的内存泄漏,需要手动结束该goroutine。通常,这种资源释放的问题可以通过使用context或通过关闭信号(利用Channel实现)来解决。 - 当
Ticker定时器完成其任务后,为了防止内存泄漏,应调用Stop方法来释放相关资源。如果未及时停止Ticker,可能导致资源持续占用。
小结
本文深入探讨了 Go 语言中的 Timer 和 Ticker 定时器,详细介绍了它们的创建方式、基本用法以及相关的方法等。此外,文章还概括了这两个定时器之间的主要区别,并强调了在使用过程中的注意事项。
在编写 Go 代码时,我们应根据不同的应用场景去选择合适的定时器。同时,我们应遵循良好的规范,特别是在定时器使用完毕后及时释放资源,对于避免潜在的内存泄漏问题尤为重要。
golang定时器之timer+ticker的更多相关文章
- 定时器之Timer
Timer中的TimerTask就是一个线程,可以一直执行下去的.可以使用Timer类的cancel方法来结束.-------------------------------------------- ...
- [19/04/12-星期五] 多线程_任务定时调度(Timer、Timetask和QUARTZ)
一.Timer和Timetask 通过Timer和Timetask,我们可以实现定时启动某个线程. java.util.Timer 在这种实现方式中,Timer类作用是类似闹钟的功能,也就是定时或者每 ...
- golang 中timer,ticker 的使用
写一个程序, 5s, 10s后能定时执行一个任务,同时能不停的处理来的消息. ------------------------------------------------------------- ...
- Jmeter 定时器之同步定时器(Synchronizing Timer)
性能测试中需要模拟多用户并发测试,此时需要用到同步定时器(Synchronizing Timer).如下图,模拟用户组的数量设置20,相当于20个用户(线程)并发 名词解释: 名称:定时器名称,可根据 ...
- Android - 定时服务 - Timer
注:在项目中,有时可能会有一些定时执行的任务,这时,一般都会在一个service中写一个定时器. 例: Service类: import java.util.Timer; import java.ut ...
- JAVA中的定时调度(Timer和TimerTask)
某些时候我们需要定时去完成一些任务,这里举一个例子:我们需要在3秒钟后打印当前系统时间,此后每隔5秒重复此操作.代码如下: import java.util.TimerTask; import jav ...
- java定时任务调度-Timer(1)
一.定义 有且仅有一个后台线程对多个业务线程进行定时定频率的调度 二. Timer ----> Timer Task (中有run();方法) 通过 new Timer().schedul ...
- Golang入门教程(二)Ubuntu16.04下安装golang(实例:Golang 定时任务管理器)
通过两种方式安装 一.通过apt-get安装1.安装 sudo apt-get install golang 2.设置GOPATH变量 GOPATH是扩展库的目录,Go先搜索标准库目录,然后搜索GOP ...
- go语言time包的学习(Time,Location,Duration,Timer,Ticker)
package main; import ( "time" "fmt" ) func main() { //time.Time代 ...
- [Swift实际操作]七、常见概念-(9)使用定时组件Timer执行定时任务
本文将为你演示计时器的使用,使用计时器可以每隔一定时间执行某个函数. 在左侧的项目导航区,打开视图控制器的代码文件:ViewController.swift现在开始编写代码,实现任务定时的功能.定义一 ...
随机推荐
- 受 LabelImg 启发的基于 web 的图像标注工具,基于 Vue 框架
受 LabelImg 启发的基于 web 的图像标注工具,基于 Vue 框架 哟,网友们好,年更鸽子终于想起了他的博客园密码.如标题所述,今天给大家带来的是一个基于 vue2 的图像标注工具.至于 ...
- 鸿蒙应用开发:环境搭建(Mac)与项目构建
2021年6月2日,华为发布了鸿蒙操作系统-HarmonyOS.它是一款"面向未来".面向全场景(移动办公.运动健康.社交通信.媒体娱乐等)的分布式操作系统.我们要基于Harm ...
- OData – Get Started 搭建单侧环境
前言 之前写过 EF Core – Get Started 搭建单侧环境, 这篇补上一个 WebApi + EF Core + OData 创建项目 dotnet new webapi -o EfCo ...
- Figma 学习笔记 – Align 对齐
Figma 有几个常用的对齐方法 从左到右 1. 左对齐 Alt + A 2.左右居中对齐 Alt + H 3.右对齐 Alt + D 4.上对齐 Alt + W 5.上下居中对齐 Alt + V 6 ...
- Springboot 项目配置 HTTPS
生成证书 输入命令 keytool -genkeypair -alias "boot" -keyalg "RSA" -keystore "boot.k ...
- SpringBoot.3中的aot.factories到底有什么用?和以前的spring.factories一样吗?
首先,我们来澄清一下 aot.factories 和 spring.factories 之间的区别.这两个文件不仅名称不同,而且在功能上也存在显著差异.接下来,我们将深入探讨这两个文件的具体作用以及它 ...
- Android Systrace 基础知识 -- 分析 Systrace 预备知识
1. 正文 1.1 线程状态查看 Systrace 会用不同的颜色来标识不同的线程状态, 在每个方法上面都会有对应的线程状态来标识目前线程所处的状态,通过查看线程状态我们可以知道目前的瓶颈是什么, 是 ...
- 动态去读 dll 文件
// 反射动态读取 dll // Assembly assembly = Assembly.LoadFile(); 路径 // Assembly assembly = Assembly.LoadFro ...
- 默认nginx.conf
user nginx; worker_processes auto; error_log /var/log/nginx/error.log notice; pid /var/run/nginx.pid ...
- Vnode 的渲染过程
在tamplate编译的时候,generater 会生成一个render 函数,通过 render 函数会把 template 内容描述成 vnode ,然后vnode 通过一系列操作转换为真实的 D ...