开门见山写一个
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时做一些灵活的操作,比如跳出循环,做一些统计打印等。