golang的timer的一些坑
golang的timer的一些坑
- 需求
- 问题
- 解决方案1:单次定时器
- 解决方案2:每天10:05:00执行
- 扩展:每周一的10点执行
需求
最近项目有一些定时任务的需求,如每天的早上10:00:00定时的去执行一些任务。
问题
但是我遇到了一些问题,就是我定时10:00:00去执行,但是这个定时器疯狂的翻滚停不下来了,代码如下
package mainimport ("fmt""time"
)func main() {//每天10:05 通报昨日报警数量统计go func() {for {// 定时器会一直执行now := time.Now()// 计算明天的时间next := now.Add(time.Hour * 24 * time.Duration(0))// 10:00 进行提醒next = time.Date(next.Year(), next.Month(), next.Day(), 10, 0, 0, 0, next.Location())fmt.Println("now:", next)fmt.Println("next:", next)fmt.Println("next.Sub(now:", next.Sub(now))timer := time.NewTimer(next.Sub(now))select {case ts := <-timer.C:fmt.Println("Start YesterdayAlarmStatistics ts=%s", ts.String())}}}()for {}
}
控制台打印
Start YesterdayAlarmStatistics ts=%s 2022-04-02 14:49:34.505853 +0800 CST m=+1.806330418
now: 2022-04-02 10:00:00 +0800 CST
next: 2022-04-02 10:00:00 +0800 CST
next.Sub(now: -4h49m34.505858s
Start YesterdayAlarmStatistics ts=%s 2022-04-02 14:49:34.505868 +0800 CST m=+1.806345209
now: 2022-04-02 10:00:00 +0800 CST
next: 2022-04-02 10:00:00 +0800 CST
next.Sub(now: -4h49m34.505872s
Start YesterdayAlarmStatistics ts=%s 2022-04-02 14:49:34.505882 +0800 CST m=+1.806359626
now: 2022-04-02 10:00:00 +0800 CST
next: 2022-04-02 10:00:00 +0800 CST...
它会疯狂的打印并不会停下来,可以看到下面这行代码里传递的值其实是个负数-4h49m34.505872s,但是定时器仍在执行,百思不得其姐
timer := time.NewTimer(next.Sub(now))
分析:
Golang 的NewTimer方法调用后,生成的timer会放入最小堆,一个后台goroutine会扫描这个堆,将到时的timer进行回调和channel。
而golang的timer的Stop方法, 是只负责把timer从堆里移除,不负责close 上面的channel。于是第一次执行完毕后,之后所有值我们传递的都是个负数,但是stop并没有关闭channel,导致代码仍在在运行,就是个无限for循环。
解决方案1:单次定时器
缺点:下面的方法会让定时器变为单次执行的定时器
package mainimport ("fmt""time"
)func main() {//每天10:05 通报昨日报警数量统计go func() {for {// 定时器会一直执行now := time.Now()// 计算明天的时间next := now.Add(time.Hour * 24 * time.Duration(0))// 10:00 进行提醒next = time.Date(next.Year(), next.Month(), next.Day(), 14, 32, 10, 10, next.Location())fmt.Println("now:", next)fmt.Println("next:", next)fmt.Println("next.Sub(now:", next.Sub(now))timer := time.NewTimer(next.Sub(now))select {case ts := <-timer.C:fmt.Println("Start YesterdayAlarmStatistics ts=%s", ts.String())b := timer.Stop()if !b {fmt.Println("==============")ts := <-timer.Cfmt.Println(ts)}}}}()for {}
}
解决方案2:每天10:05:00执行
其实是我代码里埋的坑,既然它会一直执行,但是执行时间我们总能确定吧?所以我们在本次执行之后,修改下次的执行时间就行了
go func() {for {now := time.Now()var next time.Time//每天10:05发送,//如果当前时间是10:05之前那么按当前时间生成定时器,//如果不是则说明该时间已经过了10:05,于是明天发送,按照明天的10:05生成定时器if now.Hour() < 10 || now.Hour() == 10 && now.Minute() < 5 {next = now} else {next = now.Add(time.Hour * 24)}//10:05:00 进行提醒next = time.Date(next.Year(), next.Month(), next.Day(), 10, 5, 0, 0, next.Location())logger.Info("YesterdayAlarmStatistics Now=%s Next=%s", now, next)timer := time.NewTimer(next.Sub(now))select {case ts := <-timer.C:go yourFunction()logger.Info("StartYesterdayAlarmStatistics ts=%s", ts.String())time.Sleep(120 * time.Second)timer.Stop()}}}()
扩展:每周一的10点执行
go func() {for {now := time.Now()nowWeekDay := int(now.Weekday())nowHour := now.Hour()//Current Date : 0,1,2,3,4,5,6//Next Monday : 1,7,6,5,4,3,2addDay := (8 - nowWeekDay) % 7 //进程可能在任何时候重启//这类场景无需特殊处理,上面的公式得到的addDay==0/*if nowWeekDay == 1 && nowHour < 10 { //nowHour 小于 10 说明本周的排班还未更新,需要处理一下addDay = 0}*/if nowWeekDay == 1 && nowHour >= 10 { //nowHour 大于 10 说明本周的排班已更新,准备处理下周的即可addDay = 7}// 计算明天的时间next := now.Add(time.Hour * 24 * time.Duration(addDay))// 10:00 进行提醒next = time.Date(next.Year(), next.Month(), next.Day(), 10, 0, 0, 0, next.Location())// 测试环境,每天// timer := time.NewTimer(24 * 60 * 60 * time.Second)// 正式环境,每周一timer := time.NewTimer(next.Sub(now))select {case ts := <-timer.C:go UpdateOnduty() //目前只处理这个租户logger.Info("Start UpdateOnduty_7day ts=%s", ts.String())timer.Stop()}}}()
参考链接:https://www.cnblogs.com/jiangz222/p/11622495.html
golang的timer的一些坑相关推荐
- C# System.Timers.Timer中的坑,程序异常退出后timer依然运行问题
C# System.Timers.Timer中的坑,程序异常退出后timer依然运行问题 参考文章: (1)C# System.Timers.Timer中的坑,程序异常退出后timer依然运行问题 ( ...
- Go (Golang) 语言-Golang 定时器Timer和Ticker、time.AfterFunc、time.NewTicker()实例
文章目录 Golang 定时器Timer和Ticker time.Timer time.NewTimer()实例 time.AfterFunc time.Ticker time.NewTicker() ...
- 使用Golang时遇到的一些坑
1. [致命]不是所有Panic都能捕获 我们知道Golang给开发人员提供recover()机制,对堆栈异常(panic)进行捕获并自定义其处理逻辑.下面举个例子: 构造一个除0的异常场景: 输出结 ...
- golang | windows平台golang环境搭建(过坑之后)
文章目录 背景 golang安装 环境变量的配置 开发环境环境 VSCode配置 背景 我所处的环境 windows10(详细版本无所谓) go最新版本 vscode编辑器(非IDE,IDE为gola ...
- Golang 定时器timer和ticker
两种类型的定时器:ticker和timer.两者有什么区别呢?请看如下代码: tickerpackage mainimport ("fmt""time" )fu ...
- 【golang】timer 和 ticker
文章目录 场景 timer 注意 原理 场景 Timer用于一次性,会到达1次 Ticker用于周期性,会到达N次 timer 注意 注意,如果从timer.C已经收到过了,再从timer.C收就会被 ...
- Golang 日志框架 Zap 入坑指南
文章目录 简介 Cases case 1: Hello World case 2: SugaredLogger case 3: 定制化 SugaredLogger 使用 Console 格式 修改日期 ...
- 简单图文配置golang+vscode【win10/centos7+golang helloworld+解决install failed等情况】
博客目录(阅读时间:10分钟) 一.win10 0.系统环境 1. win10配置golang环境 ①下载相关软件 ②创建gowork工作空间 ③配置环境变量(GOPATH+PATH) ④验证环境配置 ...
- 动图图解 | Go 的 timer 是如何被Runtime调度的?
本篇文章剖析下 Go 定时器的相关内容.定时器不管是业务开发,还是基础架构开发,都是绕不过去的存在,由此可见定时器的重要程度. 我们不管用 NewTimer, timer.After,还是 timer ...
最新文章
- python从0开始学编程课件_小白从零开始学编程(三)--python基本概念
- Asp.Net下的DataGrid的多层表头
- Oracle PLSQL 导出数据table xx contains one or more CLOB columns 解决方案
- usb serial converter驱动安装_让你到期的机顶盒再发挥余热,终极办法任意安装软件,三网可用...
- poj 3006 java
- window计算的chk在linux下可以读,Linux系统命令从入门到完全忘记(四)
- Kubernetes Master High Availability 高级实践
- 为啥我的Python这么慢 (一)
- python——asyncio模块实现协程、异步编程(二)
- 这里有一个让你变成技术大牛的机会
- UVA 1349 Optimal Bus Route Design (二分图最小权完美匹配)
- ijkplayer之.so文件编译过程
- 图解排序算法之堆排序(Java)
- 机器翻译 | 反向翻译 (back-translation) 笔记
- P2P风控措施和风控流程
- 华为/荣耀 笔记本 HiboardDataReport.exe应用程序错误
- 修改数据库安装的服务器 系统时间,修改数据库服务器的操作系统时间
- Bumping制程简介
- js:简单的盒子碰撞
- 分数的加减乘除(运算符重载)