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
相关产品推荐
相关产品推荐

