GO 的定时器 Timer 和定时工作 cron
上次咱们说到了 GO 中 swaggo 的利用,咱们来回顾一下
- swaggo 是什么
- swagger 是什么
- 如何应用 swaggo
- 如何测试 swaggo
要是对 GO 中 swaggo 的利用还有点趣味的话,能够查看文章 工作中后端是如何将 API 提供进来的?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 的日志如何玩
技术是凋谢的,咱们的心态,更应是凋谢的。拥抱变动,背阴而生,致力向前行。
我是 小魔童哪吒,欢送点赞关注珍藏,下次见~