首页 > 其他分享 >go timer踩坑记录,为什么,如何正确使用

go timer踩坑记录,为什么,如何正确使用

时间:2023-03-13 22:58:08浏览次数:29  
标签:Reset 记录 fmt Stop timer Println time go

业务需求:开一个循环处理外部发来的任务,如果一定时间内没有任务发过来就退出,处理完一个任务后刷新超时时间
然后我就写了这个

func handle(reqCh <-chan int) {
	a := time.NewTimer(time.Second)
	defer a.Stop()
	for {
		select {
		case <-a.C:
			fmt.Println("timeout", time.Now())
			return
		case req := <-reqCh:
			fmt.Println(req) // 业务 可能耗时较多
			fmt.Println(time.Now())
			a.Reset(time.Second)
		}
	}
}

然后发现明明req一直在发没有超时,但handle还是退出了

迷糊了一下,然后就加上了打印时间测试一下,发现退出时间基本等于req处理完后的时间,所以问题找到了:Reset没有"生效"

然后第一个想的办法是直接把Reset改成a = time.NewTimer(time.Second),问题是没有了,就是有点憨憨。

我认为go有一个比较好的地方,标准库的文档很详细,然后我就点开了Reset的文档。oh 获得了新知识....

  1. Reset有返回值. true和false含义不一样(这这里其实没有实际用处)
  2. NewTimer创建的Timer只能在Stop或者<-t.C以后调用Reset (我就是犯了这错)

然后官方给的建议是这个

if !t.Stop() {
    <-t.C
}
t.Reset(d)

并且贴心地给了一句 This should not be done concurrent to other receives from the Timer's channel.

原因是如果t被stop两次<-t.C就block住了。好的,一般来说正常人不可能去stop两次,也不太会去并发stop,所以其实问题不大

不过最大的问题是太丑了... 居然有4行
然后因为文档说了Stop或者<-t.C以后才能Reset,那我直接Stop Reset不就好了,也没和文档说的矛盾嘿嘿。 (这样做有问题吗?有错误的话希望大佬指出)

t.Stop()
t.Reset(d)

最后总结一下

  1. time.Newtimer比较重,所以能Reset就Reset
  2. Reset之前必须Stop(或者执行<-t.C)

最后最后附上几个测试例子

func TestResetTimer(t *testing.T) {
	fmt.Println(time.Now())
	a := time.NewTimer(time.Millisecond * 500)
	if !a.Stop() {
		// 这里不会执行 Stop也有返回值  不多赘述
		fmt.Println(111, time.Now())
		fmt.Println(<-a.C)
	}
	a.Reset(time.Second)

	<-a.C
	fmt.Println(time.Now())
}

func TestResetTimer2(t *testing.T) {
	a := time.NewTimer(time.Millisecond * 500)
	a.Stop()
	if !a.Stop() {
		fmt.Println(111, time.Now())
		fmt.Println(<-a.C) // fatal error: all goroutines are asleep - deadlock!
	}
}

func BenchmarkResetAndNewTimer(b *testing.B) {
	for i := 0; i < b.N; i++ {
		a := time.NewTimer(time.Second)
		select { // 加上这个是因为不能直接给a赋值
		case <-a.C:
		default:
		}
		a.Stop()
		a.Reset(time.Second * 2) // 200ns/op
		//a = time.NewTimer(time.Second * 2) // 900ns/op
	}
}

标签:Reset,记录,fmt,Stop,timer,Println,time,go
From: https://www.cnblogs.com/xiaotushaoxia/p/17213250.html

相关文章

  • [DS记录] 啥都可能有的 DS 复习
    莫队回滚莫队[Cnoi2019]数字游戏当\([x,y]\)固定,考虑\(b_i=[x\lea_i\ley]\)。答案就是\([l,r]\)中每一段极长连续\(1\)的\(\sum\dbinom{len+1}{2}......
  • Google Guava Cache
    JAVA8guava31.1-jre--- 序章Guavaisasuiteofcoreandexpandedlibrariesthatincludeutilityclasses,Google'scollections,I/Oclasses,andmuchmor......
  • 【记录】ubuntu20.04配置libvirtd远程认证
    ubuntu20.04配置libvirtd远程认证前置条件:安装virt-managersals2-bin配置/etc/libvirt/libvirtd.conf解除以下注释listen_tcp=1tcp_port="16509"listen_addr=......
  • go的泛型链表
    packagemainimport"fmt"funcMapKeys[Kcomparable,Vany](mmap[K]V)[]K{ r:=make([]K,0,len(m)) fmt.Printf("001%v,%T\n",r,r) fork:=rangem{ ......
  • django离线调用
    importdjangoimportsysimportos#获取当前项目的根路径base_path=os.path.basename(os.path.basename(os.path.abspath(__file__)))print(base_path)#将当......
  • 【django-vue】七牛云上传视频 搜索接口 支付宝sdk二次封装 下单接口 前端支付页
    目录上节回顾课程详情接口choice字段今日内容1文件存储1.1七牛云上传文件2搜索导航栏2.1Header.vue3搜索接口4搜索页面5支付宝支付介绍5.1支付测试,生成支付链接6......
  • 【建造者设计模式详解】Java/JS/Go/Python/TS不同语言实现
    简介建造者模式(BuilderPattern),也叫生成器模式,属于创建型模式。它使用多个简单的对象一步一步构建成一个复杂的对象。它允许你使用相同的创建代码生成不同类型和形式的对......
  • go mmap
    packagemainimport("fmt""os""syscall")funcF(a,buint64)uint64{r:=make([]uint64,1)cadd(r,9,8)returna+b+r[0]}funccalls(res[]ui......
  • Go 结构体
    1.type关键字定义类型别名typemyInt=intvaramyInt=1varbint=2fmt.Println(a+b)类型定义定义接口定义结构体类型判断2.结构体定义......
  • 澄江街道办配置AEE 执法记录仪,打造“智慧支撑+社区治理”新格局
    随着城市化的发展,人口不断向城市聚集,管理工作环境日渐复杂。为进一步提高街道办管理执法效率,规范街道办管理执法行为,澄江街道办为一线管理人员配置了AEEDSJ-K5高清红外现场......