【Golang】关于Go语言中的定时器原理与实战应用
✨✨ 欢迎大家来到景天科技苑✨✨
🎈🎈 养成好习惯,先赞后看哦~🎈🎈
🏆 作者简介:景天科技苑
🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。
🏆《博客》:Python全栈,Golang开发,PyQt5和Tkinter桌面开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi,flask等框架,云原生K8S,linux,shell脚本等实操经验,网站搭建,数据库等分享。所属的专栏:Go语言开发零基础到高阶实战
景天的主页:景天科技苑
文章目录
- Go语言中的定时器
- 一、Timer定时器
- 1. 创建Timer
- 2. 停止Timer
- 3. 重置Timer
- 4. time.AfterFunc
- 5. time.After
- 二、Ticker定时器
- 1. 创建Ticker
- 2. 监听Ticker事件
- 3. 停止Ticker定时器
- 三、定时器应用案例
- 1. 定时打印日志
- 2. 周期性检查系统状态
- 四、总结
Go语言中的定时器
在Go语言中,定时器是并发编程中常用的工具之一。定时器可以用于监控某个goroutine的运行时间、定时打印日志、周期性执行任务等多种场景。
Go标准库提供了两种主要的定时器:Timer(一次性定时器)和Ticker(周期性定时器)。本文将详细介绍这两种定时器的用法,并通过实际案例展示其应用场景。
一、Timer定时器
Timer定时器是一种一次性定时器,即在未来某个时刻触发的事件只会执行一次。
Timer的结构中包含一个Time类型的管道C,主要用于事件通知。
在未到达设定时间时,管道内没有数据写入,一直处于阻塞状态;到达设定时间后,会向管道内写入一个系统时间,触发事件。
1. 创建Timer
使用time.NewTimer函数可以创建一个Timer定时器。该函数接受一个Duration类型的参数,表示定时器的超时时间,并返回一个*Timer类型的指针。
看下源码,Timer结构体中,timer.C是一个时间类型的通道
package mainimport ("fmt""time"
)func main() {// func NewTimer(d Duration) *Timertimer := time.NewTimer(2 * time.Second) // 设置超时时间2秒// 先打印下当前时间fmt.Println("当前时间:", time.Now())//我们可以打印下这个只读通道的值//timer.C就是我们在定义定时器的时候,存放的时间,等待对应的时间。现在这个就是根据当前时间加2秒fmt.Println("通道里面的值", <-timer.C)
}
可以看到timer.C这个只读通道里面的值,就是通过NewTimer设置时间间隔后的时间
因此,我们可以根据时间通道,来定时将来某个时间要做的事
package mainimport ("fmt""time"
)func main() {timer := time.NewTimer(2 * time.Second) // 设置超时时间2秒<-timer.C//经过两秒后只想下面代码fmt.Println("after 2s Time out!")
}
在上述代码中,创建了一个超时时间为2秒的定时器,程序会阻塞在<-timer.C处,直到2秒后定时器触发,程序继续执行并打印“after 2s Time out!”。
2. 停止Timer
使用Stop方法可以停止一个Timer定时器。该方法返回一个布尔值,表示定时器是否在超时前被停止。
如果返回true,表示定时器在超时前被成功停止;如果返回false,表示定时器已经超时或已经被停止过。
package mainimport ("fmt""time"
)func main() {timer := time.NewTimer(2 * time.Second) // 设置超时时间2秒//这里,创建定时器后,里面执行了停止方法,肯定是在定时器超时前停止了,返回trueres := timer.Stop()fmt.Println(res) // 输出:true
}
在上述代码中,创建了一个超时时间为2秒的定时器,并立即调用Stop方法停止它。由于定时器还没有超时,所以Stop方法返回true。
3. 重置Timer
对于已经过期或者是已经停止的Timer,可以通过Reset方法重新激活它,并设置新的超时时间。Reset方法也返回一个布尔值,表示定时器是否在重置前已经停止或过期。
package mainimport ("fmt""time"
)func main() {timer := time.NewTimer(2 * time.Second)<-timer.Cfmt.Println("time out1")//经过两秒后,定时器超时了res1 := timer.Stop()//此时再stop,得到的是falsefmt.Printf("res1 is %t\n", res1) // 输出:false//然后我们重置定时器。重置成3秒后超时timer.Reset(3 * time.Second)res2 := timer.Stop()//此时再stop,由于定时器没超时,得到的是truefmt.Printf("res2 is %t\n", res2) // 输出:true
}
在上述代码中,首先创建了一个超时时间为2秒的定时器,并在超时后打印“time out1”。
然后调用Stop方法停止定时器,由于定时器已经过期,所以Stop方法返回false。
接着调用Reset方法将定时器重新激活,并设置新的超时时间为3秒。
最后再次调用Stop方法停止定时器,由于此时定时器还没有过期,所以Stop方法返回true。
4. time.AfterFunc
time.AfterFunc函数可以接受一个Duration类型的参数和一个函数f,返回一个*Timer类型的指针。在创建Timer之后,等待一段时间d,然后执行函数f。
package mainimport ("fmt""time"
)func main() {duration := time.Duration(1) * time.Secondf := func() {fmt.Println("f has been called after 1s by time.AfterFunc")}// func AfterFunc(d Duration, f func()) *Timer//等待duration时间后,执行f函数//这里是经过1秒后。执行f函数timer := time.AfterFunc(duration, f)defer timer.Stop()time.Sleep(2 * time.Second)
}
在上述代码中,创建了一个超时时间为1秒的定时器,并在超时后执行函数f。
使用defer语句确保在程序结束时停止定时器。程序会在1秒后打印“f has been called after 1s by time.AfterFunc”。
5. time.After
time.After函数会返回一个*Timer类型的管道,该管道会在经过指定时间段d后写入数据。调用这个函数相当于实现了一个定时器。
package mainimport ("fmt""time"
)func main() {ch := make(chan string)go func() {time.Sleep(3 * time.Second)ch <- "test"}()//使用select,哪个先到来,耗时时间短,执行哪个select {case val := <-ch:fmt.Printf("val is %s\n", val)// 这个case是两秒后执行// func After(d Duration) <-chan Timecase <-time.After(2 * time.Second):fmt.Println("timeout!!!")}
}
在上述代码中,创建了一个管道ch,并在另一个goroutine中等待3秒后向管道写入数据。
在主goroutine中使用select语句监听两个管道:一个是刚刚创建的ch,另一个是time.After函数返回的管道c。
由于ch管道3秒后才会有数据写入,而time.After函数是2秒超时,所以2秒后select会先收到管道c里的数据,执行“timeout!!!”并退出。
二、Ticker定时器
Ticker定时器可以周期性地不断触发时间事件,不需要额外的Reset操作。Ticker的结构中也包含一个Time类型的管道C,每隔固定时间段d就会向该管道发送当前的时间,根据这个管道消息来触发事件。
Ticker定时器是Go标准库time包中的一个重要组件。它允许你每隔一定的时间间隔执行一次指定的操作。Ticker定时器在创建时会启动一个后台goroutine,该goroutine会按照指定的时间间隔不断向一个通道(Channel)发送当前的时间值。
1. 创建Ticker
要创建一个Ticker定时器,你可以使用time.NewTicker函数。这个函数接受一个time.Duration类型的参数,表示时间间隔,并返回一个*time.Ticker类型的指针。
Ticker定时器的核心是一个通道(Channel),你可以通过监听这个通道来接收时间间隔到达的事件。
ticker := time.NewTicker(1 * time.Second)
上面的代码创建了一个每隔1秒触发一次的Ticker定时器。
2. 监听Ticker事件
要监听Ticker定时器的事件,你可以使用range关键字或者select语句来监听Ticker定时器的通道。每次时间间隔到达时,Ticker定时器的通道都会接收到一个当前的时间值。
for range ticker.C { // 在这里执行周期性任务
}
package mainimport ("fmt""time"
)func main() {ticker := time.NewTicker(1 * time.Second)//查看定时器数据类型fmt.Printf("定时器数据类型%T\n", ticker)//启动协程来监听定时器触发事件,通过time.Sleep函数来等待5秒钟,然后调用ticker.Stop()函数来停止定时器。//最后,输出"定时器停止"表示定时器已经成功停止。go func() {for range ticker.C {fmt.Println("Ticker ticked")}}()//执行5秒后,让定时器停止time.Sleep(5 * time.Second)ticker.Stop()fmt.Println("定时器停止")
}
或者,如果你需要同时监听多个通道,你可以使用select语句:
select {
case t := <-ticker.C: // 处理Ticker定时器事件
case <-stopChan: // 处理停止信号
}
package mainimport ("fmt""time"
)func main() {ticker := time.NewTicker(1 * time.Second) // 创建一个每秒触发一次的Ticker定时器defer ticker.Stop() // 确保在main函数结束时停止定时器for {select {case t := <-ticker.C:fmt.Println("Tick at", t)}}
}
每秒执行一次
3. 停止Ticker定时器
当你不再需要Ticker定时器时,你应该调用它的Stop方法来停止它。停止Ticker定时器可以释放与之关联的资源,并防止不必要的goroutine继续运行。
ticker.Stop()
停止Ticker定时器后,它的通道将不再接收任何事件。
package mainimport ("fmt""time"
)func main() {ticker := time.NewTicker(500 * time.Millisecond) // 创建一个每500毫秒触发一次的Ticker定时器timeEnd := make(chan bool) // 用于停止Ticker定时器的通道go func() {for {select {//当达到设置的停止条件式,停止循环case <-timeEnd:fmt.Println("===结束任务")breakcase t := <-ticker.C:fmt.Println("==500毫秒响应一次:", t)}}}()time.Sleep(5 * time.Second) // 主线程等待5秒钟ticker.Stop() // 停止Ticker定时器timeEnd <- true // 发送结束信号fmt.Println("===定时任务结束===")
}
持续执行5秒后,定时器停止运行
三、定时器应用案例
1. 定时打印日志
Ticker定时器的一个常见应用是定时打印日志。通过设置一个Ticker定时器,你可以每隔固定的时间间隔输出一次日志信息,从而监控程序的运行状态。
package mainimport ("fmt""time"
)func main() {ticker := time.NewTicker(5 * time.Second)defer ticker.Stop() // 确保程序结束时停止Ticker定时器for range ticker.C {// 打印当前时间作为日志fmt.Println("Current time:", time.Now())}
}
5秒打印一次
在这个例子中,我们创建了一个每隔5秒触发一次的Ticker定时器,并在一个无限循环中监听它的事件。
每次事件触发时,我们都会打印当前的时间作为日志信息。注意,由于我们在main函数中使用了defer语句来确保Ticker定时器在程序结束时被停止,所以即使循环是无限的,程序也不会因为Ticker定时器而泄漏资源。
然而,在实际应用中,你可能需要在某个条件下提前停止Ticker定时器。这时,你可以使用一个额外的通道来发送停止信号:
package mainimport ("fmt""time"
)func main() {ticker := time.NewTicker(5 * time.Second)stopChan := make(chan struct{})go func() {// 模拟一个运行一段时间的任务time.Sleep(15 * time.Second)// 发送停止信号stopChan <- struct{}{}}()for {select {case t := <-ticker.C:fmt.Println("Tick at", t)case <-stopChan:fmt.Println("Ticker stopped")ticker.Stop()return}}
}
在这个例子中,我们创建了一个额外的stopChan通道来发送停止信号。我们启动了一个goroutine来模拟一个运行一段时间的任务,并在任务完成后向stopChan发送一个停止信号。
在for循环中,我们使用select语句同时监听Ticker定时器的通道和stopChan通道。当接收到停止信号时,我们停止Ticker定时器并退出程序。
2. 周期性检查系统状态
Ticker定时器还可以用于周期性检查系统状态。例如,你可以每隔一段时间检查一次服务器的负载、内存使用情况或数据库连接数等关键指标,并在发现异常时采取相应的措施。
package mainimport ("fmt""math/rand""time"
)// 模拟检查系统状态的函数
func checkSystemStatus() {// 这里可以添加实际的检查逻辑// 例如:检查CPU使用率、内存使用情况等// 这里我们随机生成一个0到100之间的数作为模拟结果status := rand.Intn(101)fmt.Printf("System status: %d\n", status)// 假设状态大于80表示系统异常if status > 80 {fmt.Println("Warning: System status is above normal!")// 这里可以添加处理异常的逻辑// 例如:发送警报、重启服务等}
}func main() {ticker := time.NewTicker(10 * time.Second)defer ticker.Stop() // 确保程序结束时停止Ticker定时器for range ticker.C {checkSystemStatus()}
}
在这个例子中,我们创建了一个每隔10秒触发一次的Ticker定时器,并在一个无限循环中监听它的事件。
每次事件触发时,我们都会调用checkSystemStatus函数来模拟检查系统状态。checkSystemStatus函数会随机生成一个0到100之间的数作为模拟结果,并根据结果判断是否系统异常。
如果系统异常(即状态大于80),则打印警告信息,并可以在这里添加处理异常的逻辑。
同样地,你可以使用额外的通道来发送停止信号,以便在需要时提前停止Ticker定时器。
四、总结
本文详细介绍了Go语言中Timer和Ticker两种定时器的用法,并通过实际案例展示了它们的应用场景。Timer定时器适用于需要一次性触发的事件,而Ticker定时器适用于需要周期性触发的事件,希望在大家go的学习与应用上能够帮助到大家。
相关文章:

【Golang】关于Go语言中的定时器原理与实战应用
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...

matlab不小心删除怎么撤回
预设项——>删除文件——>移动至临时文件夹 tem临时文件夹下...

云原生、云计算、虚拟化概念概述
(带着批评阅读,不对的请评论区补充) 1、出现年代前后顺序 虚拟化------>云计算------>云原生 2、虚拟化 虚拟化侧重描述实现,最开始的技术是模拟、hook指令执行软件程序,后续出现了半虚拟化、CPU硬件提供虚拟化…...

【Trulens框架】用TruLens 自动化 RAG 应用项目评估测试
前言: 什么是Trulens TruLens是面向神经网络应用的质量评估工具,它可以帮助你使用反馈函数来客观地评估你的基于LLM(语言模型)的应用的质量和效果。反馈函数可以帮助你以编程的方式评估输入、输出和中间结果的质量,从而…...

互联网线上融合上门洗衣洗鞋小程序,让洗衣洗鞋像点外卖一样简单
随着服务创新的风潮,众多商家已巧妙融入预约上门洗鞋新风尚,并携手洗鞋小程序,开辟线上蓝海。那么,这不仅仅是一个小程序,它究竟蕴含着哪些诱人好处呢? 1. 无缝融合,双线共赢:小程序…...

R语言绘制三维散点图
之前我们绘制的属于二维散点图,具有两个维度通常是 x 轴和 y 轴)上展示数据点的分布。只能呈现两个变量之间的关系。而三维散点图则具有三个维度(x 轴、y 轴和 z 轴)上展示数据点的分布。可以同时呈现三个变量之间的关系ÿ…...

2014年国赛高教杯数学建模A题嫦娥三号软着陆轨道设计与控制策略解题全过程文档及程序
2014年国赛高教杯数学建模 A题 嫦娥三号软着陆轨道设计与控制策略 嫦娥三号于2013年12月2日1时30分成功发射,12月6日抵达月球轨道。嫦娥三号在着陆准备轨道上的运行质量为2.4t,其安装在下部的主减速发动机能够产生1500N到7500N的可调节推力,…...

QD1-P25 CSS 背景
本节学习:CSS 背景属性 本节视频 https://www.bilibili.com/video/BV1n64y1U7oj?p25 背景颜色 背景图片 不重复 横向重复 纵向重复 双向重复 背景图片大小 400px 600px 原图大小 显示器宽度不够时&…...

《Linux运维总结:基于ARM64+X86_64架构CPU使用docker-compose一键离线部署mongodb 7.0.14容器版分片集群》
总结:整理不易,如果对你有帮助,可否点赞关注一下? 更多详细内容请参考:《Linux运维篇:Linux系统运维指南》 一、部署背景 由于业务系统的特殊性,我们需要面向不通的客户安装我们的业务系统&…...

Java利用ChromeDriver插件网页截图(Wondows版+Linux版)
chromedriver是谷歌浏览器驱动,用来模拟谷歌运行操作的一个工具,此处主要讲解Java后端利用此插件进行网页截图,并且适配Linux部署。 环境准备 Wondows服务器或电脑 本机需安装Chrome谷歌浏览器,根据本机浏览器版本,下载对应的chr…...

无人机之交互系统篇
一、系统构成 无人机交互系统通常由多个子系统组成,包括但不限于: 多模式人机交互装置:这是人机交互系统的基础层,通常包括计算机、局域网、传感器等设备,用于实现操作员与无人机之间的数据交互和指令传递。例如&…...

MarsCode--找出数字比例超过n/2的【简单】
问题描述 给定一个长度为n的整型数组,已知其中一个数字的出现次数超过数组长度的一半,找出这个元素 输入格式 一个长度为n的数组,其中某个元素的出现次数大于n/2 输出格式 一个整数 输入样例 [1,3,8,2,3,1,3,3,3] 输出样例 3 数据范…...

Python网络爬虫快速入门指南
Python网络爬虫快速入门指南 网络爬虫,也称为网络蜘蛛,是一种自动访问互联网并提取信息的程序。Python因其简洁明了的语法和丰富的库支持,成为开发网络爬虫的理想选择。在这篇博客中,我们将探讨如何快速入门Python网络爬虫技术&a…...

C86 架构一键离线安装 docker 和 docker-compose 实战指南
C86 架构一键离线安装 docker 和 docker-compose 实战指南 文章目录 C86 架构一键离线安装 docker 和 docker-compose 实战指南一 磁盘挂载二 docker 部署1 上传安装包2 解压安装包3 安装包 docker 三 验证安装四 清除安装包五 安装包下载地址 本文提供了在 C86 架构环境下&…...

【LwIP源码学习2】调试输出相关宏
前言 本文对lwip中debug.h文件里的调试相关宏进行分析。 正文 debug.h中有3个重要的调试相关宏: LWIP_ASSERT(message, assertion) LWIP_ERROR(message, expression, handler) LWIP_DEBUGF(debug, message) 断言 LWIP_ASSERT(message, assertion) 源代码为&…...

Python 列表专题:删除元素
Python 是一种强大的编程语言,广泛应用于数据分析、Web 开发、人工智能等多个领域。而列表(List)作为 Python 中最基本的数据结构之一,扮演着非常重要的角色。列表不仅可以存储各种类型的数据,还提供了丰富的操作接口,方便我们进行数据的增删改查。本篇博文将深入探讨 Py…...

Spring Boot 快速入门与核心原理详解
引言 在上一篇文章中,我们详细探讨了 Spring 框架中的事件监听与发布机制。本文将转向 Spring Boot,介绍如何快速入门 Spring Boot,并深入探讨其核心原理。Spring Boot 是由 Pivotal 团队提供的全新框架,旨在简化 Spring 应用的初…...

UniApp 与微信小程序详细对比
UniApp 与微信小程序详细对比 1. 开发环境 微信小程序: 主要使用微信开发者工具提供模拟器、调试工具和性能监控只能开发微信小程序 UniApp: 主要使用 HBuilderX,但也支持 VS Code 等其他编辑器HBuilderX 提供可视化界面、代码提示、调试工…...

【用大模型提示工程处理NLP任务】
Batch API Prompt 工程 任务一:文本分类 任务二:情感分析 任务三:文档处理 任务四:信息抽取 任务五:机器翻译 任务六:生成任务 任务七:文本纠错 Batch API Prompt 工程 Batch API 适用于…...

适配器模式、代理模式(C++)
适配器模式: 定义:适配器模式是一种结构型设计模式,它允许接口不兼容的类一起工作。它通过将一个类的接口转换成客户端期望的另一个接口,使原本由于接口不兼容而不能一起工作的那些类可以一起工作。 代理模式: 定义&a…...

unity 2d 近战攻击判定的三种方式以及精确获取碰撞点
精确获取碰撞点 核心是获取武器碰撞盒最顶点,然后获取敌人碰撞盒距离该点最近的点 /// <summary>/// 获取获取武器前端位置 碰撞盒最左或最右顶点/// </summary>/// <param name"collider"></param>/// <param name"…...

矩形函数的傅里叶变换——从一维到二维,从连续到离散
一维连续矩形函数的傅里叶变换 二维连续矩形函数的傅里叶变换 禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》P109 2D DFT 禹晶、肖创柏、廖庆敏《数字图像处理(面向新工科的电工电子信息基础课程系…...

潜水打捞系统助力,破解汽车打捞难题
随着人类活动的不断扩展,汽车落水事故频发,成为救援工作中的一大难题。汽车因其重量和结构特性,一旦沉入水体,打捞工作将面临巨大挑战。传统的打捞方法往往效率低下,且在操作过程中可能会对汽车造成进一步的损害&#…...

【深度学习】经典的深度学习模型-01 开山之作:CNN卷积神经网络LeNet-5
【深度学习】经典的深度学习模型-01 开山之作:CNN卷积神经网络LeNet-5 Note: 草稿状态,持续更新中,如果有感兴趣,欢迎关注。。。 0. 论文信息 article{lecun1998gradient, title{Gradient-based learning applied to document r…...

LeetCode 每日一题 2024/10/7-2024/10/13
记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步 目录 10/7 871. 最低加油次数10/8 1436. 旅行终点站10/9 3171. 找到按位或最接近 K 的子数组10/10 3162. 优质数对的总数 I10/11 3164. 优质数对的总数 II10/12 3158. 求出出现两…...

ZYNQ使用XGPIO驱动外设模块(前半部分)
目录 目录 一、新建BD文档,添加ZYNQ处理器 1.BD文档: 2.在Vivado中,BD文件的生成过程通常包括以下步骤: 1)什么是Tcl Console: 3.PL部分是FPGA可编程逻辑部分,它提供了丰富的IO资源,可以用于实现各种硬件接口和功…...

【FastAdmin】全栈视角下的页面跳转实现:从原生html、javascrpt、php技术到jQuery、FastAdmin框架
全栈视角下的页面跳转实现:从原生html、javascrpt、php技术到jQuery、FastAdmin框架 1 引言 页面跳转是Web开发中的基本操作,不同的技术栈提供了不同的实现方法。本文将详细介绍在原生JavaScript、原生HTML、原生PHP、jQuery以及FastAdmin框架中实现页…...

从零开始搭建一个node.js后端服务项目
目录 一、下载node.js及配置环境 二、搭建node.js项目及安装express框架 三、集成nodemon,实现代码热部署 四、Express 应用程序生成器 一、下载node.js及配置环境 网上很多安装教程,此处就不再赘述了 版本信息 C:\Users\XXX>node -v v20.15.0…...

自定义注解和组件扫描在Spring Boot中动态注册Bean(一)
博客主页: 南来_北往 系列专栏:Spring Boot实战 在Spring Boot中,自定义注解和组件扫描是两种强大的机制,它们允许开发者以声明性的方式动态注册Bean。这种方式不仅提高了代码的可读性和可维护性,还使得Spring Boot应用的…...

如何在 IDEA 中导入 Java 项目的 Git 仓库并启动
目录 前言1. 从 Git 仓库导入 Java 项目2. 配置 Maven2.1 配置 Maven 仓库和设置文件2.2 加载依赖 3. 配置 Tomcat 并运行项目3.1 配置 Tomcat3.2 配置 Server URL3.3 启动项目 4. 常见问题与解决方法4.1 Maven 依赖无法下载4.2 Tomcat 部署失败4.3 项目启动后无法访问 结语 前…...