上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
timer
定时器核心由运行时实现,
// runtime/time.go type timer struct { when int64 period int64 f func(any, uintptr) arg any } //go:linkname startTimer time.startTimer func startTimer(t *timer) { addtimer(t) } //go:linkname stopTimer time.stopTimer func stopTimer(t *timer) bool { return deltimer(t) }
从实现上看, NewTimer
创建内核定时器放入 P.timers
堆内。
要么等调度器( schedule
)执行后移除,要么主动 Stop
提前删除。
// time/sleep.go // Interface to timers implemented in package runtime. type runtimeTimer struct { ... } type Timer struct { C <-chan Time r runtimeTimer } func NewTimer(d Duration) *Timer { c := make(chan Time, 1) t := &Timer{ C: c, r: runtimeTimer{ when: when(d), // 没有设置 period,一次性。 f: sendTime, arg: c, }, } startTimer(&t.r) return t } func (t *Timer) Stop() bool { return stopTimer(&t.r) }
定时器函数 sendTime
不会阻塞。
// sendTime does a non-blocking send of the current time on c. func sendTime(c any, seq uintptr) { select { case c.(chan Time) <- Now(): default: } }
因 Ticker
设置了 period
,所以定时器不会自动移除,而是留在堆中重复执行。
只能手工调用 Stop
将其从 P.timers
堆里删除。
// time/tick.go type Ticker struct { C <-chan Time // The channel on which the ticks are delivered. r runtimeTimer } func NewTicker(d Duration) *Ticker { c := make(chan Time, 1) t := &Ticker{ C: c, r: runtimeTimer{ when: when(d), period: int64(d), // 周期性事件,只能主动删除。 f: sendTime, // 周期性执行。 arg: c, }, } startTimer(&t.r) return t } func (t *Ticker) Stop() { stopTimer(&t.r) }
显然, Tick
没有返回实例,外部无法控制( Stop
)。
所创建定时器便一直留在堆中,孜孜不倦地重复执行。这便是 leak
原因所在。
相比之下, Timer
要好一些,总归在执行后自动移除。
// time/tick.go func Tick(d Duration) <-chan Time { return NewTicker(d).C }
泄漏模拟
观察 Stop
调用与否,内存变化。
可换成 NewTimer
、 After
做同样测试。可注入 net/http/pprof
,在线采样分析。
package main import ( "time" "runtime" ) func main() { go func() { for { t := time.NewTicker(time.Second) select { case <-t.C: default: } // 不用 defer,减少干扰。 // t.Stop() // 缩短间隔,模拟更多请求。 time.Sleep(time.Microsecond * 100) } }() // 延长测试时间。 for i := 0; i < 30; i++ { time.Sleep(time.Second * 10) runtime.GC() } } /* $ go build -o test && GODEBUG=gctrace=1 ./test */
更多详细内容,请参考《下卷:源码剖析》并发调度 / 其他 / 定时器。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论