Go Ticker正确的使用姿势
简介
Ticker是周期性定时器,即周期性的触发一个事件,通过Ticker本身提供的管道将事件传递出去。
Ticker的数据结构与Timer完全一致:
type Ticker struct { C <-chan Time r runtimeTimer }
Ticker对外仅暴露一个channel,指定的时间到来时就往该channel中写入系统时间,也即一个事件。
在创建Ticker时会指定一个时间,作为事件触发的周期。这也是Ticker与Timer的最主要的区别。
另外,ticker的英文原意是钟表的"滴哒"声,钟表周期性的产生"滴哒"声,也即周期性的产生事件。
使用场景
简单定时任务
有时,我们希望定时执行一个任务,这时就可以使用ticker来完成。
下面代码演示,每隔1s记录一次日志:
// TickerDemo 用于演示ticker基础用法 func TickerDemo() { ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for range ticker.C { log.Println("Ticker tick.") } }
上述代码中,for range ticker.C
会持续从管道中获取事件,收到事件后打印一行日志,如果管道中没有数据会阻塞等待事件,由于ticker会周期性的向管道中写入事件,所以上述程序会周期性的打印日志。
定时聚合任务
有时,我们希望把一些任务打包进行批量处理。比如,公交车发车场景:
- 公交车每隔5分钟发一班,不管是否已坐满乘客;
- 已坐满乘客情况下,不足5分钟也发车;
下面代码演示公交车发车场景:
// TickerLaunch用于演示ticker聚合任务用法 func TickerLaunch() { ticker := time.NewTicker(5 * time.Minute) maxPassenger := 30 // 每车最大装载人数 passengers := make([]string, 0, maxPassenger) for { passenger := GetNewPassenger() // 获取一个新乘客 if passenger != "" { passengers = append(passengers, passenger) } else { time.Sleep(1 * time.Second) } select { case <- ticker.C: // 时间到,发车 Launch(passengers) passengers = []string{} default: if len(passengers) >= maxPassenger { // 时间没到,车已座满,发车 Launch(passengers) passengers = []string{} } } } }
上面代码中for循环负责接待乘客上车,并决定是否要发车。每当乘客上车,select语句会先判断ticker.C中是否有数据,有数据则代表发车时间已到,如果没有数据,则判断车是否已坐满,坐满后仍然发车。
Ticker对外接口
创建定时器
使用NewTicker方法就可以创建一个周期性定时器,函数原型如下:
func NewTicker(d Duration) *Ticker
其中参数d
即为定时器事件触发的周期。
停止定时器
使用定时器对外暴露的Stop方法就可以停掉一个周期性定时器,函数原型如下:
func (t *Ticker) Stop()
需要注意的是,该方法会停止计时,意味着不会向定时器的管道中写入事件,但管道并不会被关闭。管道在使用完成后,生命周期结束后会自动释放。
Ticker在使用完后务必要释放,否则会产生资源泄露,进而会持续消耗CPU资源,最后会把CPU耗尽。更详细的信息,后面我们研究Ticker实现原理时再详细分析。
简单接口
部分场景下,我们启动一个定时器并且永远不会停止,比如定时轮询任务,此时可以使用一个简单的Tick函数来获取定时器的管道,函数原型如下:
func Tick(d Duration) <-chan Time
这个函数内部实际还是创建一个Ticker,但并不会返回出来,所以没有手段来停止该Ticker。所以,一定要考虑具体的使用场景。
错误示例
Ticker用于for循环时,很容易出现意想不到的资源泄露问题,下面代码演示了一个泄露问题:
func WrongTicker() { for { select { case <-time.Tick(1 * time.Second): log.Printf("Resource leak!") } } }
上面代码,select每次检测case语句时都会创建一个定时器,for循环又会不断的执行select语句,所以系统里会有越来越多的定时器不断的消耗CPU资源,最终CPU会被耗尽。
总结
Ticker相关内容总结如下:
- 使用time.NewTicker()来创建一个定时器;
- 使用Stop()来停止一个定时器;
- 定时器使用完毕要释放,否则会产生资源泄露;
赠人玫瑰手留余香,如果觉得不错请给个赞~
本篇文章已归档到GitHub项目,求星~ 点我即达
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
看完就懂的无痕埋点
在移动互联网时代,对于每个公司、企业来说,用户的行为数据非常重要。重要到什么程度,用户在这个页面停留多久、点击了什么按钮、浏览了什么内容、什么手机、什么网络环境、App什么版本等都需要清清楚楚。一些大厂的蛮多业务成果都是基于用户操作行为进行推荐后二次转换。另一方面是以日志的作用帮助开发者分析线上问题的一种辅助手段。 那么有了上述的诉求,那么技术人员如何满足这些需求?引出来了一个技术点-“埋点” 0x01. 埋点手段 业界中对于代码埋点主要有3种主流的方案:代码手动埋点、可视化埋点、无痕埋点。简单说说这几种埋点方案。 代码手动埋点:根据业务需求(运营、产品、开发多个角度出发)在需要埋点地方手动调用埋点接口,上传埋点数据。 可视化埋点:通过可视化配置工具完成采集节点,在前端自动解析配置并上报埋点数据,从而实现可视化“无痕埋点” 无痕埋点:通过技术手段,完成对用户行为数据无差别的统计上传的工作。后期数据分析处理的时候通过技术手段筛选出合适的数据进行统计分析。 0x02. 技术选型 1. 代码手动埋点 该方案情况下,如果需要埋点,则需要在工程代码中,写埋点相关代码。因为侵入了业务代码,对业务代...
- 下一篇
跨端开发框架深度横评
上周,Taro 团队发布了一篇《小程序多端框架全面测评》,让开发者对业界主流的跨端框架,有了初步认识。感谢 Taro 团队的付出。 不过横评这件事,要想做完善,其实非常花费时间。不是只看文档就行,它需要: 真实的动手写多个平台的测试demo,比较各个平台的功能、性能,它们的实际情况到底是不是如文档宣传的那样? 真实的学习每个框架,了解它们的学习曲线,在实际开发中遇到问题时,感受它们的文档、教程、社区生态和技服能力到底怎么样? 我们 uni-app 团队投入一周完成了这个深度评测,下面我们就分享下,实际开发不同框架的测试例遇到的问题,和最终的测试结果。 评测实验介绍 开发内容:开发一个仿微博小程序首页的复杂长列表,支持下拉刷新、上拉翻页、点赞。 界面如下: 开发版本:一共开发了6个版本,包括微信原生版、wepy版、mpvue版、taro版、uni-app版、chameleon版(以这些产品发布时间排序,下同),按照官网指引通过cli方式默认安装(应该是最新稳定版)。 测试代码开源(Github仓库地址:https://github.com/dcloudio/test-framework)...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Linux系统CentOS6、CentOS7手动修改IP地址
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- Red5直播服务器,属于Java语言的直播服务器
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- SpringBoot2整合Redis,开启缓存,提高访问速度
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- CentOS7,CentOS8安装Elasticsearch6.8.6