前置说明
Go time 包提供两类定时器,底层均基于通道 chan 实现阻塞等待:
Timer:一次性定时器,时间到仅触发 1 次;支持手动停止、重置倒计时Ticker:周期性打点器,每隔固定时间自动循环触发;只能手动停止,无 Reset
一、一次性定时器 time.Timer
1. 基础结构
go
Go
type Timer struct {
C <-chan Time // 只读通道,时间到自动写入当前时间
r runtimeTimer
}
创建语法:
go
Go
timer := time.NewTimer(时长)
// 示例:2秒定时器
timer := time.NewTimer(2 * time.Second)
核心特性:定时器到期后,只会向 timer.C 写入一次时间,通道只会有 1 条数据,读完后无新数据,再次读取会永久阻塞死锁。
2. 基础使用:等待定时器到期
写法 1:NewTimer 标准用法
go
Go
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(2 * time.Second)
fmt.Println("当前时间:", time.Now())
// 阻塞读取通道,时间未到则卡住
t := <-timer.C
fmt.Println("时间到,触发时间:", t)
}
写法 2:time.After 简化延时(底层封装 NewTimer)
适合只需要一次性延时、不需要停止 / 重置定时器的场景:
go
Go
package main
import (
"fmt"
"time"
)
func main() {
// 阻塞2秒
<-time.After(2 * time.Second)
fmt.Println("2秒延时结束")
}
注意:
time.After无法手动停止,高频循环中反复调用会大量创建定时器,增加 GC 压力,推荐复用NewTimer。
3. Timer 核心方法 Stop () 停止定时器
作用:倒计时未结束时,终止定时器,不再向通道发送数据返回值 bool:
true:定时器正常停止(未到期)false:定时器已经到期 / 已停止,停止失败
示例:提前终止定时器,子协程永远阻塞
go
Go
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(3 * time.Second)
go func() {
<-timer.C // 永远读不到数据,阻塞
fmt.Println("子协程打印")
}()
// 直接停止定时器
ok := timer.Stop()
fmt.Println("停止结果:", ok)
for {} // 阻塞主协程观察效果
}
4. Timer 核心方法 Reset (d Duration) 重置倒计时
作用:修改定时器剩余时长,重新开始倒计时返回值规则同 Stop:
true:重置前定时器还在运行(未到期)false:定时器已到期 / 已停止
go
Go
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(3 * time.Second)
time.Sleep(1 * time.Second)
// 重置为1秒倒计时
ok := timer.Reset(1 * time.Second)
fmt.Println("重置前定时器是否运行:", ok)
t := <-timer.C
fmt.Println("重置后触发时间:", t)
}
5. 易错点:Timer 只会触发一次
定时器到期后通道仅写入 1 次数据,循环读取会死锁:
go
Go
// 错误示例,会永久阻塞
timer := time.NewTimer(2 * time.Second)
for {
<-timer.C
fmt.Println("时间到")
}
如需循环定时,两种方案:
- 每次循环调用
timer.Reset() - 改用
Ticker周期打点器
二、周期打点器 time.Ticker(循环定时)
1. 基础特性
- 每隔指定时长自动重复触发 ,持续往
ticker.C写入时间 - 无 Reset 方法,只能调用
Stop()彻底关闭循环 - 不手动 Stop 会持续占用资源,用完必须停止
创建语法:
go
ticker := time.NewTicker(间隔时长)
2. 完整示例:循环计数,达到次数停止打点器
go
Go
package main
import (
"fmt"
"time"
)
func main() {
// 每秒触发一次
ticker := time.NewTicker(1 * time.Second)
i := 0
for {
<-ticker.C
i++
fmt.Println("计数 i =", i)
// 执行5次后停止打点器,退出循环
if i == 5 {
ticker.Stop()
break
}
}
fmt.Println("打点器已停止")
}
三、Timer vs Ticker 核心对比总结
表格
| 特性 | time.Timer | time.Ticker |
|---|---|---|
| 触发次数 | 仅 1 次(一次性) | 无限循环周期触发 |
| 重置 | 支持 Reset () | 不支持 Reset |
| 停止 | Stop (),未到期返回 true | Stop (),直接终止循环 |
| 适用场景 | 延时等待、超时控制 | 定时轮询、周期任务 |
| 简化写法 | time.After(d) | 无简化函数 |
四、复习速记要点
- Timer 是一次性定时器,时间到仅向 C 通道写入一次时间,重复读取会死锁;
time.After(d)底层是 NewTimer,适合简单延时,无法手动停止;timer.Stop():提前终止倒计时;timer.Reset():重新修改倒计时时长;- Ticker 循环定时,每隔固定时间自动触发,用完必须调用 Stop 释放资源;
- 两者均基于通道阻塞实现等待,读取通道前程序会卡住。