GO的定时器Timer 和定时任务cron
GO的定时器Timer 和定时任务cron
上次我们说到了GO 中 swaggo 的应用,咱们来回顾一下
- swaggo 是什么
- swagger 是什么
- 如何使用 swaggo
- 如何测试 swaggo
之后我们可以来一次
swaggo
的原理分享,细细的了解一下swaggo
是如何生成swagger
文档的 文章图片
今天咱们来看看 GO 里面的 定时器 Timer 和 定时任务 cron
咱们今天还是来看看 定时器 timer 和 定时任务 cron 如何使用,关于他们的原理,咱们后续文章会详细分享
Timer 是什么? 是 GO 中提供一个 定时器包,主要是用
time.Timer
timer 实际上是一种单一事件的定时器
也就是说,经过指定的时间后触发一个事件,这个事件通过其本身提供的 通道 进行通知 , 因为Timer只执行一次就结束,所以叫他单一事件
Timer
与Ticker
的最重要的区别之一 就是这里了大致流程是这个样子的:
Go 运行时会启动一个单独的 协程
该协程 执行了一个
timerproc
的函数,维护了一个 最小堆该协程会定期被唤醒并读取堆顶的
timer
对象,执行该 timer 对象对应的函数(就是在 timer.C
中发送一条数据,用于触发定时器)执行完毕后就会从最小堆中移除该
timer
对象咱们创建的
time.Timer
,实际上就是在这个最小堆中添加一个 timer
对象实例,那么我们需要停止定时器,也就是使用 timer.Stop
的时候,就是从这个堆里面删除对应的 timer
对象本文先不细细说明实际原理,咱们先会简单应用它,后续会详细分享
万事开头难,然后中间难,最后结尾难
文章图片
Timer 如何使用? 咱们简单看看
Timer
对应的数据结构位置在:
src/time/sleep.go:Timer
Timer
代表一次定时,时间到来后只发生一个事件只发生一次,这里尤为重要
Timer
对外仅暴露一个通道,指定的时间到了,就会往该通道中写入系统时间,时间到了就触发一次事件,只会触发一次,因为时间只会到一次type Timer struct {
C <-chan Time
r runtimeTimer
}
咱们分别从如下几个场景使用一下 Timer
- 基本使用
- Time 延时使用
- 停止定时器
- 重置定时器
咱们设置一个 1s 中的定时器,这个定时器只会触发一次
创建一个定时器:
func New*Timer*(d Duration) Timer
指定一个时间即可创建一个
Timer
,Timer
一经创建便开始计时,不需要额外的启动命令func main() {
// 创建一个 Timer
myT := time.NewTimer(1 * time.Second)
// 从通道中读取数据,若读取得到,说明时间到了
<- myT.C
fmt.Println(" 1 s 时间到")for {}
}
Time 延时使用
设置一个 1 秒的定时,再延时 2 秒
func main() {
// 创建一个 Timer
myT := time.NewTimer(1 * time.Second)
<- myT.C
fmt.Println(" 1 s 时间到 ",time.Now().Unix())// 延时 2 秒
<-time.After(2 * time.Second)
fmt.Println(" 2 s 时间到 ",time.Now().Unix())for {}
}
运行代码执行效果如下:
1 s 时间到1624757781
2 s 时间到1624757783
GO 还提供了一个函数
AfterFunc
func AfterFunc(d Duration, f func()) *Timer
也是可以做到延迟的效果,更好的是,延迟了之后,能够执行我们填入的函数
停止定时器
Timer 创建后可以随时停止,咱们可以使用time.Stop()停止定时器:
func (t *Timer) Stop() bool
Stop()
函数返回值是 bool
,要么是 true
, 要么是 false
, 代表的含义是 定时器是否超时- true
- false
写一个DEMO , 设置 1 s 的定时器
若在到了1 s ,则进行打印,说明已经超时
若没有到 1 s ,通道就已经关闭了,则未超时
func testChannelTimeout(conn chan int) bool {
// 设置 1 秒的定时器,若在到了1 s ,则进行打印,说明已经超时
timer := time.NewTimer(1 * time.Second)select {
case <-conn:
if (timer.Stop()){
fmt.Println("timer.Stop()")
}
return true
case <-timer.C: // timer 通道超时
fmt.Println("timer Channel timeout!")
return false
}
}func main() {ch := make(chan int, 1)
// 若打开如下语句,则可以正常关闭定时器
// 若注释如下语句,则关闭定时器超时
//ch <- 1
go testChannelTimeout(ch)for {}
}
上述代码中,是否关闭定时器超时,跟另外一个辅助通道息息相关
若打开如下语句,则可以正常关闭定时器
若注释如下语句,则关闭定时器超时
ch <- 1
重置定时器
开局设置一个鱼的记忆,7秒的定时器
立刻将定时器重置成 1 秒的定时器
func main() {
// 创建一个 Timer 鱼的记忆
fmt.Println(" 开始 ", time.Now().Unix())
myT := time.NewTimer(7 * time.Second)
// 重置定时器为 1 s
myT.Reset(1 * time.Second)
<-myT.C
fmt.Println(" 1 s 时间到 ", time.Now().Unix())for {}
}
运行上述代码后,效果如下:
开始1624759572
1 s 时间到1624759573
上述
Timer
都是触发一次,生效一次,这样并不能满足所有场景,例如周期性定时执行的场景就不满足了文章图片
咱们可以使用 GO 里面的 Ticker
Ticker 是什么?
Ticker
也是定时器,不过他是一个周期性的定时器,也就是说,他用于周期性的触发一个事件,通过
Ticker
本身提供的管道将事件传递出去的Ticker
对外仅暴露一个通道,指定的时间到了,就往该通道中写入系统时间,也即一个事件。此处的时间到了,只的是周期性的时间到了Ticker 如何使用? 位置在:
src/time/tick.go:Timer
type Ticker struct
和 type Timer struct {
一模一样// A Ticker holds a channel that delivers ``ticks'' of a clock
// at intervals.
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
r runtimeTimer
}
关于创建定时器 和 关闭定时器 和 上述的 Timer方法类似,咱们一起列举出来
创建
Ticker
定时器(强调:这是一个周期性的定时器)func NewTicker(d Duration) *Ticker
关闭
Ticker
定时器func (t *Ticker) Stop()
简单应用
Ticker
设置 2 秒的 周期性定时器
Ticker
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()// 若通道为空,则阻塞
// 若通道有数据,则读取
// 若通道关闭,则退出
for range ticker.C {
fmt.Println("ticker ticker ticker ...")
}
来一个通用版本的 DEMO
周期性的执行任务,我们可以灵活设置时间,和具体处理的任务
- 封装
Ticker
的调用
// 定义函数类型
type Fn func() error// 定时器中的成员
type MyTicker struct {
MyTick *time.Ticker
Runner Fn
}func NewMyTick(interval int, f Fn) *MyTicker {
return &MyTicker{
MyTick: time.NewTicker(time.Duration(interval) * time.Second),
Runner: f,
}
}// 启动定时器需要执行的任务
func (t *MyTicker) Start() {
for {
select {
case <-t.MyTick.C:
t.Runner()
}
}
}func testPrint(){
fmt.Println(" 滴答 1 次")
}func main() {
t := NewMyTick( 1 ,testPrint)
t.Start()
}
执行上述代码,运行效果:
滴答 1 次
滴答 1 次
滴答 1 次
...
触发一次的
Timer
,周期性触发的Ticker
,咱们都应用到了文章图片
cron 是什么? 看到
cron
小伙伴们应该不会陌生吧,用过 linux 的应该对 cron
还是有点想法的在
linux
里面咱们可以使用 crontab -e
来设置定时任务,GO 里面,我们也可以是使用 cron
包来设置定时任务不过,
linux
里面 上述定时任务只支持 分钟以上级别咱们的 GO 可以支持到 秒级别
文章图片
cron 如何使用? 使用的包:
"github.com/robfig/cron"
关于
cron
的基本语法和 在linux
玩的时候类似,咱们来列举一下:// 每隔1秒执行一次
*/1 * * * * ?// 每隔1分钟执行一次
0 */1 * * * ?// 每天0点执行一次
0 0 0 * * ?// 每月1号凌晨1点执行一次
0 0 1 1 * ?// 在1分、2分、3分执行一次
0 1,2,3 * * * ?// 每天的0点、1点、2点执行一次
0 0 0,1,2 * * ?
解释一下上述的一些字符:
- *
*/1 * * * * ?
第 2 个 * 就是代表 每一分钟- /
0 */1 * * * ?
表示,每一隔分钟执行一次- ,
例如秒, 可以写 1到59秒钟的任意数字,
1,3,5 * * * * ?
,指的是每一分钟的 1 , 3 ,5秒 会执行任务其中时、分、秒的可选范围是 1-59
日 可选范围是 1-31
月 可选范围是 1-12
年 可选范围是 1-12
星期 可选范围是 0-6 表示 周日 - 周六
- -
1-10/2 * * * * ?
,指每分钟的 1 -10,每隔 2 秒钟,执行任务- ?
来一个简单的例子
设置 每隔 2 秒钟 执行一次任务
func main() {
i := 0
c := cron.New()
spec := "*/2 * * * * ?"
err := c.AddFunc(spec, func() {
i++
fmt.Println("cron times : ", i)
})
if err != nil {
fmt.Errorf("AddFunc error : %v",err)
return
}
c.Start()defer c.Stop()
select {}
}
cron
用起来还是非常简单的,感兴趣的朋友,可以多多实践一下,关于他们的原理,咱么后续娓娓道来文章图片
总结
- Timer 是什么
- Timer 如何使用
- Ticker 是什么
- Ticker 如何使用
- cron 是什么
- cron 如何使用
文章图片
好了,本次就到这里,下一次 GO 的日志如何玩
技术是开放的,我们的心态,更应是开放的。拥抱变化,向阳而生,努力向前行。
【GO的定时器Timer 和定时任务cron】我是小魔童哪吒,欢迎点赞关注收藏,下次见~
推荐阅读
- 热闹中的孤独
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 一个人的旅行,三亚
- 布丽吉特,人生绝对的赢家
- 慢慢的美丽
- 尽力
- 一个小故事,我的思考。
- 家乡的那条小河
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量