You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang:Timer值类型与指针类型调用Stop后通道行为差异疑问

为什么Go中Timer值类型调用Stop后仍能从C通道读取值?

核心原因是time.Timer的Stop方法是指针接收者,两段代码中Timer变量的类型(值/指针)决定了Stop实际作用的对象完全不同。

第一段(值类型Timer)的异常行为解析

var ti time.Timer
func init() {
    ti = *time.NewTimer(3 * time.Second)
}
func main() {
    ti.Stop()
    t := <-ti.C
    fmt.Println("End", t) 
}
  • time.NewTimer()返回的是*Timer指针,指向一个底层的Timer结构体实例。你用*解引用后,把这个结构体完整复制给了全局变量ti。
  • Timer.Stop()的定义是func (t *Timer) Stop() bool——它是指针接收者方法,只有通过指针调用,才能修改原Timer实例的内部状态(比如停止后台的定时任务)。
  • 当你调用ti.Stop()时,因为ti是值类型,Go会自动生成临时指针(&ti)来调用Stop,但这个操作只作用在你复制出来的ti副本上,完全没影响到NewTimer创建的原始Timer实例。
  • 原始Timer的后台任务依然在运行,3秒后会向通道C发送时间值。而复制的ti.C和原始Timer的C是同一个通道(通道是引用类型,复制结构体不会生成新通道),所以main里能读到这个值。

第二段(指针类型Timer)的正常行为解析

var ti *time.Timer
func init() {
    ti = time.NewTimer(3 * time.Second)
}
func main() {
    ti.Stop()
    t := <-ti.C
    fmt.Println("End", t) // deadlock!
}
  • 这里ti直接持有NewTimer返回的原始Timer实例的指针,调用ti.Stop()时,直接作用在原始Timer实例上,成功停止了后台定时任务。
  • 因为定时器不会再向C发送数据,main里的t := <-ti.C会一直阻塞,最终触发死锁,这完全符合Stop方法的预期效果。

补充Go的方法调用规则

  • 若方法是指针接收者:
    • 用指针调用:直接操作指针指向的原实例,能修改其内部状态。
    • 用值调用:Go会自动取该值的地址,但操作的是这个值的副本,无法影响原始实例。
  • time.Timer这类需要修改内部状态的类型,官方设计时就要求用指针操作,NewTimer直接返回指针也是为了避免用户因复制结构体导致的异常行为。

内容的提问来源于stack exchange,提问作者lkm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 09:56:20