Go Runtime(十):Timer实现

本文源码基线为 Go 1.26.2、Linux/amd64。Go 1.23 重写了 Timer Channel 的可回收性与 Stop/Reset 保证;阅读旧版本分析时必须先确认版本边界。 Timer 不是一个独立后台 goroutine 加一张全局表。Go 1.26 把真实时间 Timer 组织在每个 P 的四叉最小堆中,由调度器和 netpoll 共同决定睡到何时、何时执行到期回调。 它同时服务 time.Sleep、time.Timer、time.Ticker、AfterFunc 和网络 Deadline。难点不只是“按时间排序”,而是并发 Stop/Reset、跨 P 修改、周期跳期、Channel 旧值以及 GC 可达性。 time包如何进入runtime time 包通过 runtime 提供的私有入口创建和修改 Timer: TEXTtime.Sleep -> runtime.timeSleep time.NewTimer -> runtime.newTimer(sendTime, channel) Timer.Stop -> runtime.stopTimer Timer.Reset -> runtime.resetTimer time.AfterFunc -> runtime.newTimer(goFunc, nil) net deadline -> runtime timer callback time.NewTimer 的源码仍写 make(chan Time, 1)。默认模式下 runtime 借助 hchan.timer 对外呈现同步、容量为零的语义;设置 GODEBUG=asynctimerchan=1 才恢复 Go 1.23 以前的异步 Timer Channel 行为。因此不能只看 make 参数推断 cap(t.C) 和 Stop/Reset 语义。 ...

August 18, 2026

Go Runtime(二):GMP与Goroutine调度

本文源码基线为 Go 1.26.2、Linux/amd64。调度器变化频繁;函数名、队列容量和分支顺序均以固定版本源码为准。 Go 调度器解决的核心问题是:怎样把大量 goroutine 映射到较少的 OS 线程,同时控制竞争、阻塞和调度延迟。 理解调度器不需要记住所有函数。先抓住三件事:执行资格属于 P,等待发生在 G 上,真正运行机器指令的是 M。 GMP模型 TEXTG(goroutine) 栈、入口函数、调度现场、状态、等待原因 M(machine) OS线程、当前G、g0、信号处理上下文 P(processor) 本地运行队列、runnext、mcache、timer、执行资格 抽象关系如下: TEXT P.local runq [G][G][G][G] | v M(OS thread) + P ----> 执行某个 G | P.runnext M 必须绑定 P 才能执行普通 Go 代码。没有 P 的 M 可以停在线程阻塞、syscall、cgo 或 runtime 的特定路径中,但不能继续执行任意用户 Go 代码。 GOMAXPROCS 控制 P 的数量。它限制的是并行执行 Go 代码的能力,不限制 goroutine 或 OS 线程的总数。 ...

August 18, 2026