golang1.23版本之前 Timer Reset方法无法正确使用

golang1.23 之前 Reset ​到底有什么问题

golang 的 time.Reset 文档中有这么一句话,为了防止文档更新而导致内容变动,这里粘贴出来:

Before Go 1.23, the only safe way to use Reset was to [Stop] and explicitly drain the timer first. See the NewTimer documentation for more details.
在Go 1.23 之前,唯一安全使用Reset函数的方式是:在使用之前调用Stop函数并且明确的从timer的channel中抽取出东西。

虽然文档中已经告诉了正确使用的方式,但是实际上在真正的代码中无法达到这个要求,参考下方代码(来源代码来源):

//consumer
go func() {
// try to read from channel, block at most 5s.
// if timeout, print time event and go on loop.
// if read a message which is not the type we want(we want true, not false),
// retry to read.
timer := time.NewTimer(time.Second * 5)
for {
// timer may be not active, and fired
if !timer.Stop() {
select {
case <-timer.C: //try to drain from the channel,尝试抽取,由于使用select,因此这里可以保证:不阻塞 + 一定抽取成功
default:
}
}
timer.Reset(time.Second * 5) //重置定时器
select {
case b := <-c:
if b == false {
fmt.Println(time.Now(), ":recv false. continue")
continue
}
//we want true, not false
fmt.Println(time.Now(), ":recv true. return")
return
case <-timer.C:
fmt.Println(time.Now(), ":timer expired")
continue
}
}
}()

在上面的代码中,我们按照文档的要求,在 timer.Reset ​之前已经调用了 Stop ​函数,且如果 Stop 成功(返回 true),还尝试抽取 timer,看起来似乎没问题的代码中仍然存在问题。

问题的关键在于:当 Ticket 触发的时候,设置定时器状态的操作和发送 channel 的操作并不是原子的,见 runOneTimer 函数

异常情况:尝试抽取 channel 在 发送 channel 之前,这样会导致 Reset 之前并没有真正的清空 timer,后一次的 timer.C 还没到触发时间就异常的触发了!

golang1.23 之前到底应该如何正确的使用 Reset

实际上简洁点就这么写,每次一个新的局部变量 Timer​ 结构体没压力,非要复用使用 Reset 的可读性太差了,对维护者不友好,而且习惯了不好的写法,哪天一不小心就写出问题了~

go func() {
for {
func() {
timer := time.NewTimer(time.Second * 2)
defer timer.Stop() select {
case b := <-c:
if !b {
fmt.Println(time.Now(), "work...")
}
case <-timer.C: // BBB: normal receive from channel timeout event
fmt.Println(time.Now(), "timeout")
}
}()
}
}()

参考:

https://tonybai.com/2016/12/21/how-to-use-timer-reset-in-golang-correctly/

https://www.v2ex.com/t/794283

golang1.23版本之前 Timer Reset方法无法正确使用的更多相关文章

  1. Git恢复之前版本的两种方法reset、revert

    实战 回退 1.删除之前的提交 git reset --hard id 推送到远程 git push -f [git log中确实删除了,但是拿到可以恢复] 2.不删除之前的提交 git revert ...

  2. ubuntu查看mysql版本的几种方法

    ubuntu查看mysql版本的几种方法 mysql 1:在终端下:mysql -V(大写) //代码 $ mysql -V mysql Ver 14.14 Distrib 5.5.46, for d ...

  3. 【转】ubuntu 配置 java jdk1.8 环境,增加多版本 jdk 和切换方法

    一.安装java jdk1.8 1.添加软件源 sudo add-apt-repository ppa:webupd8team/java 2.更新软件源 sudo apt-get update 3.安 ...

  4. git 本地库推送远程库 版本冲突的解决方法

    参考: http://blog.csdn.net/shiren1118/article/details/7761203 github上的版本和本地版本冲突的解决方法 $ git push XXX ma ...

  5. 更新xcode后插件失效问题——不针对特定版本的通用解决方法

    一.Xcode更新后插件失效的原理 1.每次更新Xcode后插件都会失效,其实插件都还在这个目录好好的躺着呢: ~/Library/Application Support/Developer/Shar ...

  6. CSS Reset方法

    CSS Reset 即重设浏览器的样式.在各种浏览器中,都会对CSS的选择器默认一些数值,譬如当h1没有被设置数值时,显示一定大小. 但并不是所有的浏览器都使用一样的数值,所以,有了CSS Reset ...

  7. Linux下查看内核、CPU、内存及各组件版本的命令和方法

    Linux下查看内核.CPU.内存及各组件版本的命令和方法 Linux查看内核版本: uname -a                        more /etc/*release       ...

  8. InputStream中通过mark和reset方法重复利用缓存

    通过缓存InputStream可重复利用一个InputStream,但是要缓存一整个InputStream内存压力可能是比较大的.如果第一次读取InputStream是用来判断文件流类型,文件编码等用 ...

  9. reset()方法的使用、jq下面reset()的正确使用方法

    reset()是 原生js的的方法,所有浏览器都支持,而且必须是form元素包括下的表单元素,但是JQuery中没有reset方法, 效果图:  错误用法: 正确用法: js用法: document. ...

  10. 2个版本并存的python使用新的版本安装django的方法

    2个版本并存的python使用新的版本安装django的方法 默认是使用 pip install django 最新版的django会提示  要求python版本3.4以上,系统默认的版本是2.7.5 ...

随机推荐

  1. 使用YARP来实现负载均衡

    YARP ("Yet Another Reverse Proxy") 是一个库,可帮助创建高性能.生产就绪且高度可自定义的反向代理服务器. YARP 是使用 ASP.NET 和 . ...

  2. 从InputStream到ByteArrayInputStream

    本篇主要分析:1.如何将byte数组适配至ByteArrayInputStream,对应与IO部分的适配器模式:2.BufferedInputStream的工作原理,对应于IO的装饰器模式,会首先研究 ...

  3. vue表格轮播插件

    1.前言 需求:制作大屏看板时,经常要展示表格数据,通常一页时放不下的,表格需要自动滚动,并维持表头固定 为何自己封装:网上的滚动组件有2类,一种传入json数据进行滚动(DataV),优点是可以做到 ...

  4. 分布式系统架构1:共识算法Paxos

    1.背景 今天开始更新分布式的文章,工作几年后还没系统的学习分布式的内容,趁着还有时间学习沉淀的时候多输出些文章 2.为什么需要分布式共识算法 思考:现在你有一份随时变动的数据,需要确保它正确存储在网 ...

  5. CMYK与RGB参数转换公式及转换方法

    1. RGB色彩模式 自然界中绝大部分的可见光谱可以用红.绿和蓝三色光按不同比例和强度的混合来表示.RGB分别代表着3种颜色:R代表红色,G代表绿色.B代表蓝色.RGB模型也称为加色模型,如图5所示. ...

  6. Git clone报错“Connection was reset, errno 10054”

    前情 最近在使用一个UI库的时候,发现其中一个BUG,于是想尝试提一个PR. 坑位 我平时习惯用https的方式拉取代码,发现在clone代码的时候一直失败,错误提示:OpenSSL SSL_read ...

  7. OS之《死锁》

    什么是死锁 一组进程中的每一个进程都在等待仅由该组进程中其他进程才能引发的事件,这样就形成死锁了. 死锁的原因 竞争不可抢占的资源 竞争可消耗资源 进程推进顺序不当 死锁产生的必要条件 1.互斥条件: ...

  8. 关于 Span 的一切:探索新的 .NET 明星: 4. Span<T> 和 Memory<T> 是如何与 .NET 库集成的?

    4. Span<T> 和 Memory<T> 是如何与 .NET 库集成的? 1. Span<T> 是什么? 2. Span<T> 是如何实现的? 3. ...

  9. 解锁 Git Log 更多实用技巧

    目前,在软件开发的协作中,Git 无疑是版本控制的王者. 而其中的 git log 命令,犹如一把强大的历史探寻之剑,能够帮助我们深入洞察项目的演进历程. 本篇将为大家整理解读几个实用的 git Lo ...

  10. 【工具】navcat无限使用

    1.打开无限试用工具所在文件夹,打开navcat安装所在文件夹 2.将破解dll文件移动到navcat安装目录下 然后就去试用软件吧,如果软件试用到期了或者快要到期就运行这个脚本就行. 按道理来说这个 ...