golang的timer的一些坑

Golang:定时器的终止与重置 作者:ReganYue 来源:恒生LIGHT云社区 Golang:定时器的终止与重置 大家好,这里是努力变得优秀的R君,这次我们继续来进行Golang系列《让我们一起Golang》,昨日有读者对定时器的终止有疑问,本次我们来了解Golang的定时器的终止与重置这也是一个比较容易理解的知识点,一起来看一看吧! 先看下面一段代码: func main() { timer := time.NewTimer(3 * time.Second) fmt.Println(time.Now(),"炸弹将. 阅读详情

需求

最近项目有一些定时任务的需求,如每天的早上10:00:00定时的去执行一些任务。

问题

但是我遇到了一些问题,就是我定时10:00:00去执行,但是这个定时器疯狂的翻滚停不下来了,代码如下

package main

import (
	"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 main

import (
	"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.C
					fmt.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,2

			addDay := (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

Go语言时间控制:定时器技术详细指南 经过前十章的探索,我们已经从基础的 time.Timer 和 time.Ticker 用法,深入到网络编程、任务调度、测试、调试和分布式系统的应用。 阅读详情

相关推荐

从拆解到调试:用CS5523改造旧款车载显示屏的完整记录(MIPI转eDP方案)

本文详细记录了使用CS5523芯片将旧款车载显示屏从MIPI接口改造为eDP驱动的完整过程。通过对比LT8911等芯片,选择CS5523实现低功耗、高兼容性的显示升级,涵盖硬件改造、固件配置及调试技巧,最终成功提升显示效果200%。

web99的专栏 343

golang定时器使用踩的坑:定时器每天执行一次

golang定时器每天定时执行

java_xxxx的博客 2591

跨境TRS投资实战:从开户到交易的完整系统解析

本文系统解析了跨境TRS(总收益互换)投资的完整实战流程。详细阐述了TRS作为境外资金投资A股的灵活工具,如何通过其独特的合约结构绕开港股通限制,并提供杠杆效应。内容涵盖从选择机构、开通账户、资金管理到核心交易操作的全过程,并强调了构建包含策略、风控与心态的完整投资系统的重要性。

weixin_29233333的博客 312

golang中的定时器使用误区,定时器回收,select

golang的定时器虽然使用起来很简单,但是依然又一些细节需要注意的。 首先来看一个例子 func s1() { var count int for { select { case <-time.Tick(time.Second * 1): fmt.Println("case1") count++ fmt.Println("count--->" , count) case <-time.Tick(time.Second * 2) : fmt.Prin

猛犸象 1371

头歌 JAVA 桥接模式实验

这样,通过客户端代码配置不同的 FileConvertor 子类和 DataHandler 子类(如 OracleHandler 或 SQLServerHandle),即可实现从不同数据库读取数据并转换成不同格式的功能,而无需修改已有代码。

m0_74091276的博客 3189

数据分析面试八股文--技能软件类

数据分析的“三板斧"指的是三个在 Python 数据分析领域广泛使用的库:NumPy,Pandas,和 Matplotlib。这三个库提供了处理和分析数据的强大工具。NumPyNumPy(Numerical Python)是一个开源 Python 库,用于支持大型多维数组和矩阵运算。此外,NumPy 还提供了大量的数学函数来操作这些数组。np.array():创建数组np.arange():返回一个有终点和起点的固定步长的排列。

zfyzfw的博客 4376

Golang并发编程篇004_Timer & Ticker定时器详解

Golang定时器基于通道机制,与goroutine配合使用,提供了简介而强大的时间控制功能。

ChineHe的博客 2952

优秀的 Verilog/FPGA开源项目介绍(二十六)- ISP (图像信号处理)

介绍查看《ISP算法及架构分析介绍》今天项目的顺序就是先介绍几个关于ISP算法架构的项目,包括python、matlab、c等高级语言实现的ISP算法架构,最后介绍一个FPGA实现ISP的项目。openISPhttps://github.com/cruxopen/openISP项目介绍用python实现的ISP架构,该架构涉及到的知识如下:下图是最新实现的模块:主要模块如...

OpenFPGA的博客 4944

[Golang] timer可能造成的内存泄漏

背景 前两天,跟一位学长交流Golang;然后,他突然问我:你知道timer可能造成内存泄漏嘛? 当时,甚是一脸懵逼,毕竟之前写的Agent测了好久,都没发现这个问题啊。今天,就索性了解了下。这里先说下结论,timer的误用可能造成某些等待timer的Goroutine无法正常退出,导致资源无法释放;(ps. 虽然的确算是内存泄漏,但是对于我这种写C出身的人来说,感觉还是怪怪的) 接下来进入正题,...

sai_j的博客 5341

【Docker】完美解决拉取镜像超时报错:ERROR: Get https://registry-1.docker.io/v2/

因为我们下载的镜像是外网的镜像资源,所以下载的速度会非常的慢,甚至大概率下载时会报错,所以需要我们配置一个国内的服务器镜像地址,国内服务器镜像地址有很多,这里咱们就说一下阿里镜像加速器的配置方法。

liu_chen_yang的博客 12万+

【PointNet++】使用Open3D对分割后的结果可视化

PointNet++中的局部分割网络预测后是没有可视化文件的,所以需要自己通过编写Open3D或者PCL点云算法去可视化,在本人经过查找翻阅修改后,实现了网络输出点云分割后结果的可视化。已实现推测单个点云,并可视化。

纵马踏花向自由 2453

关于go语言,使用channel和timer定时执行以及优雅遇到的坑

 问题描述: 1、我在主进程中,通过go启动了两个协程来处理labor的运行。主进程定位为g0,两个协程分别定义为g1和g2 2、同时在主进程中,通过运行 runConfigChangedMonitor 来定时监控配置文件是否发生变化,如果发生变化就退出 3、退出方法为Shutdown(),在该方法内部,调用labor的Shutdown()来关闭labor的运行,之后通过向chan bool...

lylee1981 1072

168、OIS光学防抖系统设计:陀螺仪标定、音圈马达驱动与滚珠式/悬丝式结构选型

本文总结了OIS光学防抖系统设计中的关键问题与解决方案。作者通过实际案例指出,陀螺仪标定需重视零偏、灵敏度和温度补偿问题,音圈马达驱动需采用电流环控制并优化相位补偿。在机械结构选型方面,滚珠式适合大角度补偿但存在回差问题,悬丝式精度高但受重力影响,需根据应用场景选择。系统联调建议从开环到闭环逐步验证,注意陀螺仪与驱动频率匹配。最后给出5条经验建议:关注陀螺仪信噪比而非分辨率、预留标定余量、重视机械结构公差、配备专业调试工具、协调OIS与电子防抖的分工配合。文章强调OIS调试需针对不同模组特性进行反复测试与标

zzfmlxs的博客 41

Go内存泄露排查实战:RSS一路飙到8G,居然是 defer 踩了闭包坑

上周三清晨,告警平台突然发来高危通知:检索服务的 K8s Pod 内存占用(RSS)呈 45 度角持续攀升,从初始的 500MB 一路飙到了 8GB 容器上限。随后 K8s 触发了 OOM Killed 强制杀死容器并重启。然而新 Pod 启动不到两小时,内存又再次走上了同样的泄露死路。最诡异的是,Go 官方上报的HeapAlloc(堆分配内存)只有 600MB,但系统层的 RSS 物理内存却耗光了整整 8GB,两者相差了十几倍。

guoyizhongxing的博客 4487

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

timer定时器_探索Golang定时器的陷阱

Go语言中文网,致力于每日分享编码、开源等知识,欢迎关注我,会有意想不到的收获!所谓陷阱,就是它不是你认为的那样,这种认知误差可能让你的软件留下隐藏Bug。刚好Timer就有3个陷阱,我们会讲1)Reset的陷阱和2)通道的陷阱,3)Stop的陷阱与Reset的陷阱类似,自己探索吧。Reset的陷阱在哪Timer.Reset()函数的返回值是bool类型,我们看一个问题三连:它的返回值代表什么呢?...

weixin_39863631的博客 265

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Go 语言里那些“看似人畜无害,实则暗藏杀机”的技巧

现象本质应对姿势len("你好") ≠ 2字符串是字节序列用[]rune或utf8包nil接口 ≠nil接口是 (type, data)避免在接口中返回具体类型的 nilrange map中改 map迭代顺序不确定别在 loop 里改 map,或改完再 rangetime.After泄露每次新建 Timer用StopGo 像一位严谨又幽默的导师:💬——“孩子,路还长,咱们慢慢来。📌记住“简单”的语言 ≠ 简单的程序。但只要你知道这些“微妙”,你就能写出——

weixin_44058951的博客 835

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Go语言时间管理利器:深入解析time模块的实战技巧

时间处理统一化:始终坚持使用time.Time类型传递时间值时区显式声明:处理跨时区业务时,统一转换为UTC时间进行计算资源及时释放:Timer/Ticker必须配合defer使用,避免goroutine泄漏格式化标准化:团队统一时间格式字符串,推荐使用RFC3339格式性能敏感场景:优先使用进行时间戳计算通过深入掌握time包的各种特性,开发者可以轻松应对各种复杂的时间处理场景。记住,好的时间管理不仅能提升代码质量,更能避免许多潜在的线上故障。现在就去你的项目中实践这些技巧吧!

白僧的博客 567

clockwork高级技巧:如何优雅地控制时间流逝与阻塞等待

clockwork是一个专为Go语言设计的假时钟库,它允许开发者在测试环境中精确控制时间流逝,避免了传统测试中依赖真实时间带来的不确定性和长时间等待问题。无论是单元测试还是集成测试,clockwork都能帮助你构建更可靠、更高效的时间相关代码测试场景。 ## 为什么选择clockwork?三大核心优势 在Go语言开发中,处理时间相关的代码一直是测试的难点。clockwork通过模拟时钟解决了这

gitblog_00985的博客 1076

Go 高并发微观控制:彻底打通锁机制、原子操作与定时器的底层进化

本文探讨了Go语言在高并发场景下的微观控制机制,重点分析了锁机制、原子操作和定时器的底层优化及适用场景。通过秒杀计数器的案例对比,展示了原子操作相比互斥锁在性能上的显著优势(耗时降低7倍),同时指出原子操作仅适用于单变量保护的局限性,而互斥锁则能保护复杂业务逻辑块。此外,文章介绍了Go定时器在网络超时控制中的关键作用,强调不同并发场景下需选择合适的同步机制以实现最佳性能。这些微观层面的控制技术是构建高性能Go并发程序的基础。

jieyucx的博客 317

Golang Ticker Reset异常的坑

timer.Reset无效, 执行两次

编程是艺术,极致是信仰,分享是连接世界的方式。 889

Go Timer使用方法

为什么80%的码农都做不了架构师?>>> ...

weixin_34185364的博客 4264

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

Golang 定时器包括:一次性定时器(Timer)和周期性定时器(Ticker)。编程中经常会通过timer和ticker。 Golang time.Timer and time.Ticker 参考URL: https://www.jianshu.com/p/2b4686b8de4a time.Timer 首先我们看Timer的结构定义: type Timer struct { C <-chan Time r runtimeTimer } 其中有一个C的只读chann

西京刀客 1万+

Golang timer定时器

Golang timer定时器   timer 计时器 用于在指定的Duration类型时间后调用函数或计算表达式。 如果只是想指定时间之后执行,使用time.Sleep() 使用NewTimer(),可以返回的Timer类型在计时器到期之前,取消该计时器 直到使用&lt;-timer.C发送一个值,该计时器才会过期 定义计数器 timer := time.NewTimer(time...

秋天的博客 1万+

golang 重要知识:定时器 timer

在 Go 里有很多种定时器的使用方法,像常规的 Timer、Ticker 对象,以及经常会看到的 time.After(d Duration) 和 time.Sleep(d Duration) 方法,今天将会介绍它们的使用方法以及会对它们的底层源码进行分析,以便于在更好的场景中使用定时器。

yue_xin_tech 1553
上一篇: vscode里拉取golang项目,点击函数名跳转不到函数定义
下一篇: golang之flag.String、flag.Parse
java_xxxx
博客等级 码龄9年 127粉丝 99原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值