一文带你探索Golang计时器的奥秘
作者:金刀大菜牙 发布时间:2024-02-17 22:35:37
Golang 是一种简洁高效的编程语言,拥有强大的并发支持和丰富的标准库。在 Golang 中,计时器(timer)是一种常见的工具,用于定期执行某个任务或者在指定时间后触发某个事件。本文将深入探讨 Golang 计时器的实现原理和使用方法,帮助大家更好地理解和应用计时器。
1. Golang 计时器基础
在开始之前,我们需要了解 Golang 中计时器的基本概念和使用方法。
1.1 计时器的创建和启动
在 Golang 中,计时器可以通过 time.NewTimer(d Duration) *Timer 方法来创建,其中 d 参数表示计时器的触发时间间隔。计时器创建后需要调用 timer.Start() 方法来启动计时器。
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(1 * time.Second)
timer.Start()
// 其他逻辑代码
<-timer.C
fmt.Println("计时器触发")
}
1.2 计时器的停止
通过 timer.Stop() 方法可以停止计时器的运行。如果在计时器触发之前调用了 timer.Stop() 方法,那么计时器将被停止,不会触发事件。
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(1 * time.Second)
timer.Start()
// 其他逻辑代码
timer.Stop()
fmt.Println("计时器已停止")
}
1.3 计时器的重置
计时器可以通过 timer.Reset(d Duration) bool 方法来重置计时器的触发时间间隔。该方法返回一个布尔值,表示计时器是否成功重置。
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(1 * time.Second)
timer.Start()
// 其他逻辑代码
timer.Reset(2 * time.Second)
fmt.Println("计时器已重置")
}
2. Golang 计时器实现原理
了解了计时器的基本使用方法后,我们来深入探讨 Golang 计时器的实现原理。Golang 的计时器是基于堆的实现,通过调整堆中元素的顺序来确定最近的触发时间。
2.1 堆结构
Golang 的堆(heap)是一种完全二叉树的数据结构,其中每个节点的值都大于或等于其子节点的值。在堆中,根节点的值最小(或最大),因此也称为最小堆(或最大堆)。
2.2 计时器的堆实现
当我们创建一个计时器时,Golang 内部会创建一个计时器对象(Timer)。该计时器对象包含以下字段:
when:表示计时器的触发时间,以纳秒为单位。
period:表示计时器的触发间隔,以纳秒为单位。
f:表示计时器触发时要执行的函数。
arg:表示传递给计时器触发函数的参数。
seq:表示计时器的序号。
Golang 使用一个全局的计时器堆(timerHeap)来管理所有的计时器对象。该堆是一个切片,其中每个元素都是一个计时器对象的指针。
当我们创建并启动一个计时器时,计时器对象会被添加到计时器堆中,并根据触发时间进行调整,以确保堆的顶部元素是最近要触发的计时器。
在计时器触发的时刻,Golang 会从计时器堆中取出堆顶的计时器对象,并执行其触发函数。随后,如果计时器是周期性的,则会根据触发间隔重新计算下一次触发时间,并将计时器对象再次插入堆中。
2.3 计时器的堆调整
当我们启动计时器、重置计时器或者停止计时器时,计时器堆需要进行相应的调整,以保持堆的性质。
启动计时器:将计时器对象插入堆中,并根据触发时间进行调整。
重置计时器:首先将计时器对象从堆中移除,然后更新触发时间和周期,并重新插入堆中进行调整。
停止计时器:将计时器对象从堆中移除。
Golang 的计时器堆实现了 container/heap 接口,通过调用 heap.Init(h *timerHeap)、eap.Push(h *timerHeap, x interface{}) 和 heap.Pop(h *timerHeap) interface{} 等方法,可以方便地对计时器堆进行初始化、插入和删除操作。
3. Golang 计时器的高级用法
除了基本的使用方法,Golang 计时器还提供了一些高级的用法,可以更灵活地应用计时器。
3.1 计时器的单次触发
默认情况下,Golang 的计时器是周期性的,即在触发后会根据设定的触发间隔再次触发。如果我们只需要计时器触发一次,则可以在触发函数中手动停止计时器。
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(1 * time.Second)
timer.Start()
go func() {
<-timer.C
fmt.Println("计时器触发")
timer. Stop()
}()
// 其他逻辑代码
time.Sleep(2 * time.Second) // 等待计时器触发或超时
}
在上面的示例中,我们在匿名的 goroutine 中等待计时器触发,并在触发后手动调用 timer.Stop() 方法停止计时器。这样,计时器只会触发一次。
3.2 计时器的超时控制
有时候我们需要在一定的时间范围内执行某个操作,如果超过了指定的时间仍未完成,则需要进行超时处理。Golang 的计时器可以很好地支持这种场景。
package main
import (
"fmt"
"time"
)
func main() {
timeout := 3 * time.Second
done := make(chan bool)
go func() {
// 模拟一个耗时操作
time.Sleep(2 * time.Second)
done <- true
}()
select {
case <-done:
fmt.Println("操作完成")
case <-time.After(timeout):
fmt.Println("操作超时")
}
}
在上面的示例中,我们使用 time.After(timeout) 创建了一个计时器,当超过指定的超时时间后,该计时器会触发,从而触发超时处理逻辑。
3.3 计时器的延迟执行
有时候我们希望在一定的延迟时间后执行某个操作。Golang 的计时器可以帮助我们实现这个需求。
package main
import (
"fmt"
"time"
)
func main() {
delay := 2 * time.Second
timer := time.NewTimer(delay)
go func() {
<-timer.C
fmt.Println("延迟执行")
}()
// 其他逻辑代码
time.Sleep(3 * time.Second) // 等待计时器触发或超时
}
在上面的示例中,我们创建了一个计时器,并在匿名的 goroutine 中等待计时器触发。通过调整计时器的延迟时间,可以灵活控制操作的延迟执行。
4. 总结
本文深入探讨了 Golang 计时器的实现原理和使用方法。我们了解了计时器的基本概念和操作方法,以及计时器堆的实现原理。此外,还介绍了计时器的单次触发、超时控制和延迟执行等高级用法。通过学习和应用这些知识,我们可以更好地理解和使用 Golang 的计时器,为大家提供精确的定时触发和调度功能。希望本文能对大家在 Golang 计时器的学习和实践中起到指导和帮助作用。
来源:https://juejin.cn/post/7230728447207030842


猜你喜欢
- 当需要远程办公时,使用pycharm远程连接服务器时必要的。PyCharm提供两种远程调试(Remote Debugging)的方式:配置远
- 今天要查询所有realname的username,psw,gname,tel 表结构: 表t1 字段名:t1_id,username,psw
- 一、项目说明在日常生活中,我们经常会存取一些朋友们的丑照,在这个项目中,我们以萌萌哒的熊猫头作为背景,然后试着在背景图上加入朋友们的照片。效
- 本文实例讲述了python中enumerate函数用法。分享给大家供大家参考。具体分析如下:今日发现一个新函数 enumerate 。一般情
- 1、注意:pool必须在 if __name__ == '__main__' 下面运行,不然会报错2、多进程内出现错误会直接
- 用DIV+CSS可以作出很多不同形状的角形;以下我只写了几个;CSS没有优化;是为了让大家看得更清一些;以下是一些小三角的形状:这是第一个小
- Microsoft? SQL Server? 2000 的可用版本如下:SQL Server 2000企业版作为生产数据库服务器使用。支持
- 作为一位不懂代码的业余网页制 * 好者,常常羡慕专业程序人员在浏览器中编制出的效果超酷的一些多媒体作品。唉,无奈程序那东东,酶涩南学,非一日之
- 编码规范Python 编码规范重要性的原因用一句话来概括就是:统一的编码规范可以提高开发效率。无论你是 编程者,还是 阅读者,好的规范能让你
- 在并发编程中,资源的分配和回收是一个很重要的问题。对于频繁的分配和回收,会造成大量的开销。而 Go 语言的 Sync.Pool 是一个可以帮
- 脚本之家下载:JetBrains DataGrip 2020.1 免费中文正式版(附汉化包+安装教程) 最新DataGrip202
- pandas 中 inplace 参数在很多函数中都会有,它的作用是:是否在原对象基础上进行修改inplace = True:不创建新的对象
- Python 模块EasyGui详细介绍前言:在Windows想用Python开发一些简单的界面,所以找到了很容易上手的EasyGui库。下
- 当我们学习surface命令时,已经看到了三维作图的一些端倪。在matlab中我么可以调用mesh(x,y,z)函数来产生三维图像。首先,我
- PHP Too few arguments to function的解决过去自定义函数的时候如果参数不足,则会抛出一个警告,但是在7.1开始
- 前言kettle是一款免费开源的、可视化的、国际上比较流行的、功能强大的ETL必备工具,在ETL这一方面做的还不错,下面介绍一下基于win1
- python中使用requests模块http请求时,发现中文参数不会自动的URL编码,并且没有找到类似urllib (python3)模块
- 如何在庞大的数据中高效的检索自己需要的东西?本篇内容介绍了Python做出一个大数据搜索引擎的原理和方法,以及中间进行数据分析的原理也给大家
- 本文实例为大家分享了OpenCV3.3+Python3.6实现图片高斯模糊的具体代码,供大家参考,具体内容如下高斯模糊高斯模糊(英语:Gau
- 前言jsonpath是一个可以在复杂的json数据中根据用户指定的规则找到特定数据的库。本文利用jsonpath对接口进行封装,旨在写一个对