目录

  • ticker源码分析
    • 关键源码
      • ticker核心源码
      • startTimer stopTimer sentTime 核心源码
    • 源码解读
      • 分析
      • 注意事项
      • ticker优化关闭思路
  • MyTicker代码
    • 代码
    • 代码效果

ticker源码分析

关键源码

ticker核心源码

package timeimport "errors"type Ticker struct {C <-chan Time r runtimeTimer
}func NewTicker(d Duration) *Ticker {if d <= 0 {panic(errors.New("non-positive interval for NewTicker"))}c := make(chan Time, 1)t := &Ticker{C: c,r: runtimeTimer{when:   when(d),period: int64(d),f:      sendTime,arg:    c,},}startTimer(&t.r)return t
}func (t *Ticker) Stop() {stopTimer(&t.r)
}

startTimer stopTimer sentTime 核心源码

func sendTime(c interface{}, seq uintptr) {// Non-blocking send of time on c.// Used in NewTimer, it cannot block anyway (buffer).// Used in NewTicker, dropping sends on the floor is// the desired behavior when the reader gets behind,// because the sends are periodic.select {case c.(chan Time) <- Now():default:}
}// startTimer adds t to the timer heap.
//go:linkname startTimer time.startTimer
func startTimer(t *timer) {if raceenabled {racerelease(unsafe.Pointer(t))}addtimer(t)
}// stopTimer removes t from the timer heap if it is there.
// It returns true if t was removed, false if t wasn't even there.
//go:linkname stopTimer time.stopTimer
func stopTimer(t *timer) bool {return deltimer(t)
}func deltimer(t *timer) bool {if t.tb == nil {//若创建了runtimeTimer未加入调度池为falsereturn false}tb := t.tb//移除runtimeTimer lock(&tb.lock)removed, ok := tb.deltimerLocked(t)unlock(&tb.lock)if !ok {badTimer()}return removed
}

源码解读

分析

ticker通过startTimer用于在timeproc加入一个调度任务
ticker通过stopTimer用于在timeproc加入去掉调度任务
ticker通过sentTime接收timeproc的调度信号,移除调度任务后并不会关闭ticker通道
ticker起停逻辑为创建并加入调度任务池/从调度任务池删除

注意事项

  1. ticker必须关闭 ,即底层将runtimeTimer移除系统定时任务池,否则ticker的runtimeTimer持续运行,无法回收。
  2. ticker关闭后通道C不能关闭,且使用select且会造成阻塞,即ticker只需要将runtimeTimer移除定时任务池,通道C并没有关闭,使用select时将会阻塞在<-C。不关闭C的原因是底层timerporc sendtime概率性出现给关闭的通道发送。

ticker优化关闭思路

组合形式拓展ticker
通过另外通道来打断select阻塞

MyTicker代码

代码

package mainimport ("fmt""sync""time"
)const (YYYYMMDDHHMISS = "2006-01-02 15:04:05"
)type MyTicker struct {*time.Ticker               //扩展定时器interval     time.Duration //定时周期fn           func()        //回调chn          chan bool     //关闭信号status       bool          //状态      true表示启动 false表示非启动
}//设置状态值
func (m *MyTicker) setStatus(status bool) {m.status = status}//获取状态值
func (m *MyTicker) getStatus() bool {return m.status
}//NewTicker interval秒级周期 fn回调函数
func NewTicker(interval int64, fn func()) *MyTicker {m := &MyTicker{interval: time.Duration(interval) * time.Second,fn:       fn,chn:      make(chan bool),}return m
}//Stop 关闭定时器
func (m *MyTicker) Stop() {fmt.Println(time.Now().Format(YYYYMMDDHHMISS), "关闭定时器...")       //打印if !m.getStatus() {fmt.Println(time.Now().Format(YYYYMMDDHHMISS), "定时已经关闭")    //打印return}//发送关闭信号m.chn <- true
}//Stop 启动定时器
func (m *MyTicker) Start() {fmt.Println(time.Now().Format(YYYYMMDDHHMISS), "启动定时器...")      //打印if m.getStatus() {fmt.Println(time.Now().Format(YYYYMMDDHHMISS), "定时已经开启")   //打印return}//启动携程监听timerporc调度 tiker 以及 自定义关闭信号go func() {//启动 tickerm.Ticker = time.NewTicker(m.interval)m.setStatus(true)                    fmt.Println(time.Now().Format(YYYYMMDDHHMISS), "定时启动")      //打印//跳出for循环时 关闭tickerdefer m.Ticker.Stop()defer m.setStatus(false)defer fmt.Println(time.Now().Format(YYYYMMDDHHMISS), "定时关闭")//打印//阻塞监听调度信号for {select {case <-m.Ticker.C://监听ticker 信号 调用任务go m.fn()case <-m.chn://监听信号  跳出for 执行deferreturndefault:}}}()
}func main() {ticker := NewTicker(1, func() {fmt.Println(time.Now().Format(YYYYMMDDHHMISS), "定时器执行")})ticker.Stop()                 //检测 stop未启动tickertime.Sleep(10 * time.Second)ticker.Start()                //检测 starttime.Sleep(10 * time.Second)ticker.Start()                //检测 start已启动tickertime.Sleep(10 * time.Second)ticker.Stop()                 //检测 stoptime.Sleep(10 * time.Second)ticker.Start()                //检测 重启开启tickertime.Sleep(10 * time.Second)ticker.Stop()                 //检测 重新关闭tickertime.Sleep(10 * time.Second)select {}
}

代码效果

2020-07-18 22:37:38 关闭定时器...
2020-07-18 22:37:38 定时已经关闭
2020-07-18 22:37:48 启动定时器...
2020-07-18 22:37:48 定时启动
2020-07-18 22:37:49 定时器执行
2020-07-18 22:37:50 定时器执行
2020-07-18 22:37:51 定时器执行
2020-07-18 22:37:52 定时器执行
2020-07-18 22:37:53 定时器执行
2020-07-18 22:37:54 定时器执行
2020-07-18 22:37:55 定时器执行
2020-07-18 22:37:56 定时器执行
2020-07-18 22:37:57 定时器执行
2020-07-18 22:37:58 启动定时器...
2020-07-18 22:37:58 定时已经开启
2020-07-18 22:37:58 定时器执行
2020-07-18 22:37:59 定时器执行
2020-07-18 22:38:00 定时器执行
2020-07-18 22:38:01 定时器执行
2020-07-18 22:38:02 定时器执行
2020-07-18 22:38:03 定时器执行
2020-07-18 22:38:04 定时器执行
2020-07-18 22:38:05 定时器执行
2020-07-18 22:38:06 定时器执行
2020-07-18 22:38:07 定时器执行
2020-07-18 22:38:08 定时器执行
2020-07-18 22:38:08 关闭定时器...
2020-07-18 22:37:48 定时关闭      //defer导致 定时启动与定时关闭时间一致
2020-07-18 22:38:18 启动定时器...
2020-07-18 22:38:18 定时启动
2020-07-18 22:38:19 定时器执行
2020-07-18 22:38:20 定时器执行
2020-07-18 22:38:21 定时器执行
2020-07-18 22:38:22 定时器执行
2020-07-18 22:38:23 定时器执行
2020-07-18 22:38:24 定时器执行
2020-07-18 22:38:25 定时器执行
2020-07-18 22:38:26 定时器执行
2020-07-18 22:38:27 定时器执行
2020-07-18 22:38:28 关闭定时器...
2020-07-18 22:38:18 定时关闭      //defer导致 定时启动与定时关闭时间一致
fatal error: all goroutines are asleep - deadlock!goroutine 1 [select (no cases)]:// 说明go携程全部回收
main.main()/Users/zyj/go/src/zyj.com/ticker/main.go:104 +0x15f

参考
[1]: https://my.oschina.net/renhc/blog/3027376/print
[2]: http://xiaorui.cc/archives/6109

go扩展ticker实现优雅起停相关推荐

  1. 项目上如何处理优雅启停的问题?

    什么是优雅启停的问题? 优雅启停是指在软件运行过程中,通过一定的方式来停止或重启软件,以避免对系统造成不必要的负担或影响到正在进行的业务.优雅启停通常需要解决以下几个问题: 如何正确地停止应用程序:在 ...

  2. (五)使用IK分词器、扩展ik词库和停词库

    使用IK分词器 集成ik分词器 https://mp.csdn.net/postedit/93602713 实体类PosEntity  /** 省略了getter.setter*/ class Pos ...

  3. 初识ES-IK分词器的拓展和停用词典

    扩展词词典 随着互联网的发展,"造词运动"也越发的频繁.出现了很多新的词语,在原有的词汇列表中并不存在.比如:"奥力给"等. 所以我们的词汇也需要不断的更新,I ...

  4. C#开发笔记之04-如何用C#优雅的计算个人所得税?

    C#开发笔记概述 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/960 访问. 首先,要对个人所得税的计算方式了解之后再 ...

  5. [guzzlehttp/guzzle]使用起来更优雅的HTTP客户端

    在处理业务时,我们总是会发起一个http请求,比如请求远程接口,或者下载一个文件.很显然,在PHP中需要使用CURL,但是curl写起来实在是太不舒服了,又难写,也不易阅读.实际上PHP有很多扩展可以 ...

  6. springBoot+actuator 实现优雅的停止服务器

    springBoot+actuator 实现优雅的停止服务器 前言 正文 前言 最新使用了springboot框架作为web应用开发,目前已经上生产环境,但是对了每周的代码更新是痛苦的,更加可怕的是之 ...

  7. linux下gdb单步调试

    用 GDB调试程序 GDB 概述 ---- GDB 是 GNU开源组织发布的一个强大的 UNIX下的程序调试工具.或许,各位比较喜欢那种图形界面方式的,像 VC. BCB等 IDE的调试,但如果你是在 ...

  8. 浅谈RSocket与响应式编程

    简介: RSocket是高效一个二进制的网络通讯协议,能够满足很多场景下使用.另外,RSocket也是一个激进的响应式捍卫者,激进到连API都跟响应式无缝集成.本文我们将和大家分享RSocket与响应 ...

  9. 使用 Packer、Ansible 和 Terraform 构建不可变的基础设施

    在容器编排领域,Kubernetes 已成为事实上的标准,而容器镜像 (Docker Image) 作为容器技术栈中最关键的创新之一,极大的推动了企业内部 Devops 运动的进程. 容器镜像所具有的 ...

最新文章

  1. 在SpringBoot中使用Spring Session解决分布式会话共享问题
  2. VB读取INI配置文件各方资料整合
  3. Codeforces Round #504 E - Down or Right 交互题
  4. java esc_java – Swing:当按下ESC键时如何关闭对话框?
  5. JAVA集合(四、ConcurrentHashMap)
  6. matlab中input输入多个数_python怎么一次输入两个数
  7. 2021河北计算机专接本考试复习资料
  8. 常用的网络上的 webservice 地址
  9. 如何获取某个网站IP地址?
  10. JDBC bug : You must configure either the server or JDBC driver
  11. 为什么设置了面容ID,仍然需要输入密码解锁iPhone?
  12. Oracle查询成绩高于成绩,Oracle认证考试成绩查询方法
  13. ElasticSearch-6.8.11实践笔记
  14. 反正切函数的应用解题报告
  15. 2023年美赛C题Wordle预测问题一建模及Python代码详细讲解
  16. 基于单目和低成本GPS的车道定位方法
  17. 多序列比对要多久时间_ClustalW----多序列比对分析(一)
  18. C++11多线程第三篇:线程传参详解,detach()大坑,成员函数做线程参数
  19. 一行代码解决网站防挂IFRAME木马方案,小鸽子序列(灵儿)
  20. 网易云音乐正式启动IPO 值不值五分之二个腾讯音乐?

热门文章

  1. 详解super()函数
  2. android+照相软件,韩国很火的照相app
  3. 程序设计基础c语言邹启明,【基础强化】2020年秋季学期程序设计C语言项目顺利结课...
  4. Redis教程–事务机制基本介绍
  5. Ubuntu20.04下运行LOAM系列:A-LOAM、LeGO-LOAM、LIO-SAM 和 LVI-SAM
  6. IOS手机安装完fiddler证书后依然无法上网
  7. 软件人员推荐书目(都是国外经典书籍!!!)
  8. git配置管理生成多个ssh的key
  9. 有5个人坐在一起,问第五个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第 3个人大2岁。问第三个人,又说比第2人大两岁。问第2个人,说比第一个人大两岁。最后 问第一个人,他说是10岁。请问第
  10. iPhone像素点和常用控件尺寸