开门见山写一个
- package main
- import (
- "fmt"
- "github.com/robfig/cron"
- "log"
- "strings"
- "time"
- )
- func CronTask() {
- log.Println("******** ******* *******")
- }
- func CronTest() {
- log.Println("Starting Cron...")
- c := cron.New()
- c.AddFunc("* * * * * *", CronTask) //2 * * * * *, 2 表示每分钟的第 2s 执行一次
- c.Start()
- t1 := time.NewTimer(time.Second * 10) // ?time.Second * 10 啥意思? *100 行吗?
- for {
- select {
- case <-t1.C:
- fmt.Println("Time now:", time.Now().Format("2006-01-02 15:04:05")) // 为何要专门制定这个时间
- t1.Reset(time.Second * 10)
- }
- }
- }
- func main() {
- fmt.Println(strings.Repeat("START", 15))
- CronTest()
- fmt.Println(strings.Repeat("END", 15))
- }
核心的定时器代码就 3 行
- c := cron.New()
- c.AddFunc("* * * * * *", CronTask)
- c.Start()
那后面那些代码时作甚的?
一开始看到示例代码时, 有个疑惑, 如代码中注释
t1 := time.NewTimer(time.Second * 10)
这里 time.Second*10 是干啥的? 是否可以写成 * 100 呢, 改了后原来是可以的, 那更疑惑了既然都行为啥还要写个这个?
还有后面的 for-select-case 也是一脸懵逼~~~~
运行代码, 从结果反推下原理吧, 一次执行结果
- START START START START START START START START START START START START START START START
- 2020/05/01 07:38:07 Starting Cron...
- 2020/05/01 07:38:08 ******** ******* *******
- 2020/05/01 07:38:09 ******** ******* *******
- 2020/05/01 07:38:10 ******** ******* *******
- 2020/05/01 07:38:11 ******** ******* *******
- 2020/05/01 07:38:12 ******** ******* *******
- 2020/05/01 07:38:13 ******** ******* *******
- 2020/05/01 07:38:14 ******** ******* *******
- 2020/05/01 07:38:15 ******** ******* *******
- 2020/05/01 07:38:16 ******** ******* *******
- 2020/05/01 07:38:17 ******** ******* *******
- Time now: 2020-05-01 07:38:17
- 2020/05/01 07:38:18 ******** ******* *******
- 2020/05/01 07:38:19 ******** ******* *******
- 2020/05/01 07:38:20 ******** ******* *******
- 2020/05/01 07:38:21 ******** ******* *******
- 2020/05/01 07:38:22 ******** ******* *******
- 2020/05/01 07:38:23 ******** ******* *******
- 2020/05/01 07:38:24 ******** ******* *******
- 2020/05/01 07:38:25 ******** ******* *******
- 2020/05/01 07:38:26 ******** ******* *******
- 2020/05/01 07:38:27 ******** ******* *******
- Time now: 2020-05-01 07:38:27
- 2020/05/01 07:38:28 ******** ******* *******
以上是运行的片段, 有两大发现
有 START START START... 没有 END END END ....: 说明了代码在执行时阻塞在定时器里, 定时器没有执行完, 永远不会执行 END
Time now 打出来的间隔正好是 10s
哦, 原来 time.NewTimer 是个定时器, 当这个时间间隔完了后再重新打开一个. for-select-case 这一块目的是阻塞流程, 不让程序结束. 理解对吗
如果是这样, 去掉 for-select-case 执行第一个定时器时也可以停 10s, 是这样吗? 试验下: 屏蔽掉 for-select-case, 输出
- START START START START START START START START START START START START START START START
- 2020/05/01 07:56:22 Starting Cron...
- END END END END END END END END END END END END END END END
打脸了, 看来阻塞主要靠 for-select-case 实现, 那原理是什么呢?
去掉 t1.Reset 效果咋样呢?
- t1 := time.NewTimer(time.Second * 10) // ?time.Second * 10 啥意思? *100 行吗?
- for {
- fmt.Println("hihihihi")
- select {
- case <-t1.C:
- fmt.Println("hello")
- }
- }
输出
- START START START START START START START START START START START START START START START
- 2020/05/01 08:12:21 Starting Cron...
- hihihihi
- 2020/05/01 08:12:22 ******** ******* *******
- 2020/05/01 08:12:23 ******** ******* *******
- 2020/05/01 08:12:24 ******** ******* *******
- 2020/05/01 08:12:25 ******** ******* *******
- 2020/05/01 08:12:26 ******** ******* *******
- 2020/05/01 08:12:27 ******** ******* *******
- 2020/05/01 08:12:28 ******** ******* *******
- 2020/05/01 08:12:29 ******** ******* *******
- 2020/05/01 08:12:30 ******** ******* *******
- 2020/05/01 08:12:31 ******** ******* *******
- hello
- hihihihi
- 2020/05/01 08:12:32 ******** ******* *******
- 2020/05/01 08:12:33 ******** ******* *******
- 2020/05/01 08:12:34 ******** ******* *******
- 2020/05/01 08:12:35 ******** ******* *******
- 2020/05/01 08:12:36 ******** ******* *******
更蒙了, 去掉 reset, 运行完第一个定时器 10s, 非但没听, 还直接执行起来了, 没停了
for 循环里的 print 不是刷刷的一大片, 而是和 case 命中时一期打, 看来是时候了解下 select-case 的原理了
select case
按惯例先上个例子
- package main
- import (
- "fmt"
- "strings"
- )
- func SelectTest() {
- intChan := make(chan int, 1)
- stringChan := make(chan string, 1)
- intChan <- 123456
- stringChan <- "hello"
- select {
- case value := <-intChan:
- fmt.Println(value)
- case value := <- stringChan:
- fmt.Println(value)
- }
- }
- func main() {
- fmt.Println(strings.Repeat("START", 15))
- SelectTest()
- fmt.Println(strings.Repeat("END", 15))
- }
执行多次可以看到, 输出的结果是 123456,"hello" 不定
select 语法
每个 case 都必须是个通信
如果一个通信可进行它就执行, 其他被忽略
如果有多个 case 可执行, 就会随机的选择一个执行
如果没有 case 可执行, 如果如果有 default, 执行 default 语句; 否则就阻塞, 直到有某个通信可行
这里还是有很多问题, 单开一节弄清楚 select 语句
再回到一开始的定时任务
回顾正题, 定时原理解析
- t1 := time.NewTimer(time.Second * 10)
- for {
- select {
- case <-t1.C:
- fmt.Println("Time now:", time.Now().Format("2006-01-02 15:04:05"))
- t1.Reset(time.Second * 10)
- }
- }
生成一个定时器 t1, 执行 for 循环, 一开始定时时间 (10s) 未到, 也没有阻塞任务, 它就阻塞在 case 中;
定时时间到了, 则执行 case 中语句;
然后又重新恢复定时时长
重新走 for 循环, 还是重复上面的故事
上面代码中尝试把 case 中的 t1.Reset 去掉, 结果也是定时任务不同的执行, 原因是执行 case 中的语句后, 接着执行 for 循环, 由于没有新通信过来(case 语句永远无法满足), 同时没有 default 语句, 所以同样可以阻塞再次.
相比在 case 中 t1 Reset, t1 Reset 更灵活些, 因为可以再每次重新满足 case 时做一些灵活的操作, 比如跳出循环, 做一些统计打印等.
来源: https://www.cnblogs.com/kaituorensheng/p/12812469.html