【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的学习与应用上能够帮助到大家。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/445980.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

结构体 超详解

目录 1. 结构体的声明与创建 1.1 结构体类型的定义声明&#xff08;类型&#xff09; 1.2 结构体变量的创建和初始化&#xff08;变量&#xff09; 1.3 结构体变量的特殊声明&#xff08;类型和变量&#xff09; 1.3.1 定义时创建变量 1.3.2 结构体的不完全声明&#xff…

解决重写QSilder::sliderPress后点击位置与滑块显示位置不一样的问题

如下代码所示&#xff0c;我是用的是事件过滤器&#xff0c;也可以重写QSlider。 bool KuGouApp::eventFilter(QObject *watched, QEvent *event) {if(watched ui->progressSlider) {if (event->type()QEvent::MouseButtonPress) //判断类型{auto mouseEvent…

XILINX MIG驱动

简介 框架图 本章节主要针对MIG读写做详细介绍,首先创建BLOCK DESIGN,工程连接如下图所示: MIG IP介绍 DATAMOVER的配置这里不再做介绍,结合上篇文章讲到DATAMOVER对BRAM进行读写操作,这里通过AXI桥再加一个MIG模块,MIG模块的配置和说明如下: 1、Clock Period:…

FPAG学习(5)-三种方法实现LED流水灯

目录 1.移位实现LED流水灯 1.1创建工程及源文件代码 1.1.1源代码 1.1.2仿真代码 1.1.3仿真 1.2实验结果 1.2.1总结 2.循环移位实现LED流水灯 3.38译码器实现LED流水灯 3.1原理 3.2源程序 1.移位实现LED流水灯 1.1创建工程及源文件代码 1.1.1源代码 利用计数器计数到…

Mybatis Plus连接使用ClickHouse也如此简单

通过阅读列式数据库ClickHouse官网&#xff0c;不难看出它有支持JDBC规范的驱动jar包&#xff0c;可以直接集成到Object Relational Mapping框架等&#xff0c;下面我用SpringBootMybatisPlus环境连接ClickHouse来演示一下 集成步骤 1.Maven引入ClickHouse提供的JDBC依赖 <…

手写mybatis之返回Insert操作自增索引值

前言 技术的把控&#xff0c;往往都是体现在细节上&#xff01; 如果说能用行&#xff0c;复制粘贴就能完成需求&#xff0c;出错了就手忙脚乱。那你一定不是一个高级开发&#xff0c;对很多的技术细节也都不了解。 目标 在前面所有的章节内容对 ORM 框架的实现中&#xff0c;其…

水库抽样算法(大数据算法作业)

时隔一个多月&#xff0c;终于想起来写大数据算法基础的实验报告&#xff0c;主要是快截止了&#xff0c;hh 这两天加急把这个报告写完了~ 接下来&#xff0c;写一写证明过程&#xff08;参考书籍&#xff1a;高等教育出版社《数据科学与工程算法基础》&#xff09;主要代码以…

如何优雅的通过Spring Boot+Redission对订单实现定时关闭

简介 在电子商务及支付相关平台中&#xff0c;常规流程是首先生成订单或支付请求&#xff0c;用户随后会在规定时间内完成支付。如果用户未能在预设时限内完成支付动作&#xff0c;系统通常会执行相应的过期处理机制&#xff0c;即自动取消未支付的订单。 此外&#xff0c;这…

圈子系统APP小程序H5该如何设置IM?

搭建圈子系统的常见问题,以及圈子论坛系统的功能特点 社交圈子论坛系统的概念 圈子小程序源码 多客圈子系统 圈子是什么软件 跟进圈一个系统的软件 为圈子系统APP小程序H5设置IM&#xff08;即时通讯&#xff09;&#xff0c;需要遵循一系列步骤来确保通讯功能的稳定、安全和高…

Centos基线自动化检查脚本

此脚本是一个用于检查Linux系统安全配置的Bash脚本。它通过多项安全标准对系统进行评估&#xff0c;主要检查以下内容&#xff1a; IP地址获取&#xff1a;脚本首先获取主机的IP地址&#xff0c;确保其以10.115开头。 密码策略检查&#xff1a; 检查最小密码长度&#xff08;P…

yum仓库安装rabbitmq

yum仓库安装rabbitmq 1、配置yum仓库 vim /etc/yum.repos.d/rabbitmq.repo # In /etc/yum.repos.d/rabbitmq.repo## ## Zero dependency Erlang ##[rabbitmq_erlang] namerabbitmq_erlang baseurlhttps://packagecloud.io/rabbitmq/erlang/el/7/$basearch repo_gpgcheck1 gpg…

甲方安全和乙方安全的区别

信息安全工作&#xff0c;总会被人分成甲方和乙方&#xff0c;甲乙方原本只是商务层面需方和供方的代称&#xff0c;在安全领域&#xff0c;成了做公司内部安全和为客户提供安全的区别。 通常意义上&#xff0c;什么是甲方安全人员呢&#xff1f;就是在非安全业务的公司从事信…

从秒级到小时级:TikTok等发布首篇面向长视频理解的多模态大语言模型全面综述

文章链接&#xff1a;https://arxiv.org/pdf/2409.18938 亮点直击 追踪并总结从图像理解到长视频理解的MM-LLMs的进展;回顾了各种视觉理解任务之间的差异&#xff0c;并强调了长视频理解中的挑战&#xff0c;包括更细粒度的时空细节、动态事件和长期依赖性;详细总结了MM-LLMs在…

基于Raspberry Pi人脸识别自动门

人脸识别自动门 简介 在当今数字化时代&#xff0c;智能家居安全变得越来越重要。今天&#xff0c;我要向大家介绍一个结合了安全性与便利性的项目——人脸识别自动门。这个项目通过在门上实施基于面部识别的高级安全系统&#xff0c;使用摄像头验证房主的面部&#xff0c;自…

非线性降维方法与概率图模型

文章目录 摘要Abstract1.降维的动机1.1 线性方法方法1.1.1 主成分分析&#xff08;PCA)1.1.2 线性判别分析(LDA)1.1.3 线性降维方法中的不足 2.基于流形学习的非线性降维2.1 ISOMAP(Isometric feature mapping)2.2 LLE(locally linear embedding)2.3 LE(Laplacian Eigenmap)拉普…

Leetcode 1203. 项目管理

1.题目基本信息 1.1.题目描述 有 n 个项目&#xff0c;每个项目或者不属于任何小组&#xff0c;或者属于 m 个小组之一。group[i] 表示第 i 个项目所属的小组&#xff0c;如果第 i 个项目不属于任何小组&#xff0c;则 group[i] 等于 -1。项目和小组都是从零开始编号的。可能…

在docker的容器内如何查看Ubuntu系统版本

文章目录 写在前面一、问题描述二、解决方法参考链接 写在前面 自己的测试环境&#xff1a; docker 一、问题描述 由于 lsb_release -a 只能查看自己电脑&#xff08;宿主机&#xff09;的系统版本&#xff0c;如果在docker的容器内又应该如何查看Ubuntu系统版本呢&#xff…

mac 桌面版docker no space left on device

报错信息 docker pull镜像时报&#xff1a; failed to register layer: Error processing tar file(exit status 1): write /home/admin/oceanbase_bak/bin/observer: no space left on device 解决 增加 docker 虚拟磁盘大小。 调整完点击重启即可。

高校学科竞赛平台开发:SpringBoot技术选型与应用

3系统分析 3.1可行性分析 通过对本高校学科竞赛平台实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本高校学科竞赛平台采用SSM框架&#xff0c;JAVA作为开发语…

C#|.net core 基础 - 删除字符串最后一个字符的七大类N种实现方式

今天想通过和大家分享如何删除字符串最后一个字符的N种实现方法&#xff0c;来回顾一些基础知识点。 01第一类、字符串方式 这类方法是通过string类型自身方法直接实现。 1、Substring方法 相信大多数人第一个想到的可能就是这个方法。Substring方法是字符串内置方法&#…