当前位置: 首页 > 工具软件 > Game-Timer > 使用案例 >

go time.NewTimer 注意事项

阎彬炳
2023-12-01

time的常见用法介绍

1. timer := time.NewTimer(time.Second * 2)

注册一个计时器,两秒之后,会向timer.C中写入一个数据

2. res := timer.Stop()

停止定时器的触发,如果已经触发过了,返回false,否则返回true,可以理解成有没有成功阻止触发该计时器
注意:该操作只会尝试阻止触发,不会删除定时器中的channel

3. <-timer.C

从定时器的channel中获取一个值,等待计时器被触发时,会自动往该channel中写入数据

4. res := timer.Reset(time.Second * 3)

重置一个计时器,三秒之后会被触发,如果该定时器还没有被触发,返回true,否则返回false

time的使用推荐方案

func Run() {
	timer := time.NewTimer(time.Second * 1)
	ch := make(chan bool, 1)

	go func() {
		for {
			ch <- true
			time.Sleep(time.Second * 1)
		}
	}()

	for {
		res := timer.Reset(time.Second * 1)
		fmt.Println("timer reset", res, time.Now().Unix())

		fmt.Println("")
		fmt.Println("-----------------------")

		select {
		case _, ok := <-timer.C:
			if ok {
				fmt.Println("time c", time.Now().Unix())
			} else {
				fmt.Println("time c not ok", time.Now().Unix())
			}
		case <-ch:
			// 推荐手动调用关闭timer,虽然timer从长远的角度看不会内存泄漏,但它的释放时间比较久,调用停止后,可以加快它的释放时间
			// 这个非常重要,也是项目中遇到的坑
			res := timer.Stop()
			fmt.Println("ch timer stop", res, time.Now().Unix())
		}
	}
}

time的细节推敲

细节1
func Run() {
	timer := time.NewTimer(time.Second * 1)

	time.Sleep(2 * time.Second)

	res := timer.Stop()
	fmt.Println(res, time.Now().Unix())

	timer.Reset(time.Second * 3)
	fmt.Println("reset c", time.Now().Unix())
	<-timer.C
	fmt.Println("time c", time.Now().Unix())
	// <-timer.C

	fmt.Println("game Over", time.Now().Unix())
}
控制台输出内容:
PS D:\code\cursor_files> go run main.go
false 1681903204		-> false 已经触发过了
reset c 1681903204  	-> 所有的时间都相同,是因为timer虽然被重置,
time c 1681903204   	-> 但timer.C中的值是存在的,所以能立即获取到
game Over 1681903204
细节2
func Run() {
	timer := time.NewTimer(time.Second * 1)

	time.Sleep(2 * time.Second)

	res := timer.Stop()
	fmt.Println(res, time.Now().Unix())

	timer.Reset(time.Second * 3)
	fmt.Println("reset c", time.Now().Unix())
	<-timer.C
	fmt.Println("time c", time.Now().Unix())
	<-timer.C

	fmt.Println("game Over", time.Now().Unix())
}

控制台输出内容:
PS D:\code\cursor_files> go run main.go
false 1681903457		-> false 已经触发过了
reset c 1681903457  	-> 最后的时间和之前的相差3秒,是因为同1的情况,
time c 1681903457   	-> 第二次从timer.C中获取值时,因为定时器的时间还没有到,
game Over 1681903460	-> 所以会等待3秒后才会触发
细节3 推荐使用方案
func Run() {
	timer := time.NewTimer(time.Second * 1)

	time.Sleep(2 * time.Second)
	// <-timer.C // 从timer中把数据取出来了

	res := timer.Stop()
	fmt.Println(res, time.Now().Unix())
	// 推荐这种写法,确保在停止后,重置前,timer.C中的数据被取出,从而避免重置timer后,会立即触发的问题
	if !res {
		select {
		case <-timer.C:
			fmt.Println("1111")
		default:
		}
	}

	timer.Reset(time.Second * 3)
	fmt.Println("reset c", time.Now().Unix())
	<-timer.C
	fmt.Println("time c", time.Now().Unix())
	// <-timer.C // 如果再次打开,将会因为无人在往timer.C中写入数据,导致主线程死锁异常

	fmt.Println("game Over", time.Now().Unix())
}

控制台输出内容:
PS D:\code\cursor_files> go run main.go
false 1681904597		-> false 已经触发过了
reset c 1681904597  	-> stop之后,如果是关闭的已经触发过的定时器,就会先尝试非阻塞的从timer.C
time c 1681904600   	-> 中读取,第二次从timer.C中获取值时,因为定时器的时间还没有到,
game Over 1681904600	-> 所以会等待3秒后才会触发

总结

具体实现细节可以查看官方的实现,从介绍中去推敲和测试,进而比较深入的认知其实现。
 类似资料: