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的一些坑相关推荐

  1. C# System.Timers.Timer中的坑,程序异常退出后timer依然运行问题

    C# System.Timers.Timer中的坑,程序异常退出后timer依然运行问题 参考文章: (1)C# System.Timers.Timer中的坑,程序异常退出后timer依然运行问题 ( ...

  2. Go (Golang) 语言-Golang 定时器Timer和Ticker、time.AfterFunc、time.NewTicker()实例

    文章目录 Golang 定时器Timer和Ticker time.Timer time.NewTimer()实例 time.AfterFunc time.Ticker time.NewTicker() ...

  3. 使用Golang时遇到的一些坑

    1. [致命]不是所有Panic都能捕获 我们知道Golang给开发人员提供recover()机制,对堆栈异常(panic)进行捕获并自定义其处理逻辑.下面举个例子: 构造一个除0的异常场景: 输出结 ...

  4. golang | windows平台golang环境搭建(过坑之后)

    文章目录 背景 golang安装 环境变量的配置 开发环境环境 VSCode配置 背景 我所处的环境 windows10(详细版本无所谓) go最新版本 vscode编辑器(非IDE,IDE为gola ...

  5. Golang 定时器timer和ticker

    两种类型的定时器:ticker和timer.两者有什么区别呢?请看如下代码: tickerpackage mainimport ("fmt""time" )fu ...

  6. 【golang】timer 和 ticker

    文章目录 场景 timer 注意 原理 场景 Timer用于一次性,会到达1次 Ticker用于周期性,会到达N次 timer 注意 注意,如果从timer.C已经收到过了,再从timer.C收就会被 ...

  7. Golang 日志框架 Zap 入坑指南

    文章目录 简介 Cases case 1: Hello World case 2: SugaredLogger case 3: 定制化 SugaredLogger 使用 Console 格式 修改日期 ...

  8. 简单图文配置golang+vscode【win10/centos7+golang helloworld+解决install failed等情况】

    博客目录(阅读时间:10分钟) 一.win10 0.系统环境 1. win10配置golang环境 ①下载相关软件 ②创建gowork工作空间 ③配置环境变量(GOPATH+PATH) ④验证环境配置 ...

  9. 动图图解 | Go 的 timer 是如何被Runtime调度的?

    本篇文章剖析下 Go 定时器的相关内容.定时器不管是业务开发,还是基础架构开发,都是绕不过去的存在,由此可见定时器的重要程度. 我们不管用 NewTimer, timer.After,还是 timer ...

最新文章

  1. python从0开始学编程课件_小白从零开始学编程(三)--python基本概念
  2. Asp.Net下的DataGrid的多层表头
  3. Oracle PLSQL 导出数据table xx contains one or more CLOB columns 解决方案
  4. usb serial converter驱动安装_让你到期的机顶盒再发挥余热,终极办法任意安装软件,三网可用...
  5. poj 3006 java
  6. window计算的chk在linux下可以读,Linux系统命令从入门到完全忘记(四)
  7. Kubernetes Master High Availability 高级实践
  8. 为啥我的Python这么慢 (一)
  9. python——asyncio模块实现协程、异步编程(二)
  10. 这里有一个让你变成技术大牛的机会
  11. UVA 1349 Optimal Bus Route Design (二分图最小权完美匹配)
  12. ijkplayer之.so文件编译过程
  13. 图解排序算法之堆排序(Java)
  14. 机器翻译 | 反向翻译 (back-translation) 笔记
  15. P2P风控措施和风控流程
  16. 华为/荣耀 笔记本 HiboardDataReport.exe应用程序错误
  17. 修改数据库安装的服务器 系统时间,修改数据库服务器的操作系统时间
  18. Bumping制程简介
  19. js:简单的盒子碰撞
  20. 分数的加减乘除(运算符重载)

热门文章

  1. 教你cad版本怎么用转换器转换操作
  2. 获取打印机分辨率_为孩子准备的第一台口袋打印机,喵喵机P2S评测
  3. Python:文本分析必备—搜狗词库
  4. docker制作镜像
  5. 气传导耳机和骨传导耳机哪个好?气传导耳机要优于骨传导
  6. C语言常见字符串处理string.h库函数strstr、strchr、strcat、strcmp、strcpy、strlen的介绍
  7. SQL 发送Email
  8. 软通动力入职考试----全套
  9. 第二阶段--团队冲刺--第十天
  10. Linux粘滞位简析