[{"content":"","date":"2026-05-12","externalUrl":null,"permalink":"/tags/channel/","section":"Tags","summary":"","title":"Channel","type":"tags"},{"content":"","date":"2026-05-12","externalUrl":null,"permalink":"/tags/go/","section":"Tags","summary":"","title":"Go","type":"tags"},{"content":"","date":"2026-05-12","externalUrl":null,"permalink":"/categories/golang%E6%BA%90%E7%A0%81%E7%90%86%E8%A7%A3/","section":"Categories","summary":"","title":"Golang源码理解","type":"categories"},{"content":"","date":"2026-05-12","externalUrl":null,"permalink":"/tags/select/","section":"Tags","summary":"","title":"Select","type":"tags"},{"content":"","date":"2026-05-12","externalUrl":null,"permalink":"/tags/time/","section":"Tags","summary":"","title":"Time","type":"tags"},{"content":"第一次看到 time.After 出现在 select 里时，我没太理解：它凭什么能和普通 channel 一起被监听？\nTimer 大家应该都在学习 goroutine 和 channel 时了解过。一个比较经典的写法是：开启 goroutine 获取结果，main goroutine 同时监听结果和超时退出。\ngo func() { time.Sleep(time.Second * 2) result \u0026lt;- 1 }() select { case \u0026lt;-result: log.Println(\u0026#34;done\u0026#34;) case \u0026lt;-time.After(time.Second * 3): log.Println(\u0026#34;timeout\u0026#34;) } close(result) 当时我对 time.After 这个函数很疑惑：为什么它能和 result 通道一样被 select 监听？\n后来我了解到了 timer 这个数据类型。它可以理解为一个定时器，到时间后自动返回一个信号。\ntimer := time.NewTimer(time.Second * 3) defer timer.Stop() select { case \u0026lt;-timer.C: log.Println(\u0026#34;3s到了\u0026#34;) case \u0026lt;-ctx.Done(): log.Println(\u0026#34;请求取消/超时\u0026#34;) } Go 的 timer 里最重要的是它的 C 字段，因为 C 本身就是一个 channel，用来接收定时信号。\n实际上，time.NewTimer 会在 Go runtime 里创建一个底层 timer，也就是创建一个定时事件。到了参数指定的时间后，runtime 会向用户态的 timer.C 发送一个信号。\n用户态代码用 select 监听 channel。监听到信号后，当前 goroutine 停止阻塞并被唤醒，然后执行对应 case 里的逻辑。\n回到最开始的问题，time.After 就很好理解了：它大概就是返回了一个定时器的 timer.C，让当前 goroutine 去监听这个 channel。\nfunc After(d Duration) \u0026lt;-chan Time { return NewTimer(d).C } 源码部分和猜想基本一致，time.After 只是创建了一个 timer，然后返回它的 C 字段。\ntimer 还有一个需要注意的重点：timer.Stop。\n不知道大家有没有注意到，使用 time.NewTimer 后通常会跟一个 defer timer.Stop()。\ntimer := time.NewTimer(time.Second * 3) defer timer.Stop() select { case \u0026lt;-timer.C: log.Println(\u0026#34;3s到了\u0026#34;) case \u0026lt;-ctx.Done(): log.Println(\u0026#34;请求取消/超时\u0026#34;) } 从上面的代码可以看出来，如果还没有到 3 秒，但 ctx 被 cancel 了，这个函数就会提前退出。\n那为什么要 Stop 呢？\n主要原因是，每次 NewTimer 都会在底层创建一个定时事件，这个定时事件实际是分配在堆上的。虽然函数退出了，但创建出来的 runtime timer 不会立刻消失。等到触发时间到达时，底层 timer 仍然会向用户态的 timer.C 发送信号，只是这时候已经没有人接收了。\n如果放到高并发场景里，这个问题会更明显。\n比如我的选课项目中，如果每秒有 1 万个请求，每个请求都创建 1 个 timer，但大部分请求 1 秒就结束了。如果不调用 Stop，runtime 里就会积压大量没有意义的定时事件，最终可能导致堆内存上涨，GC 压力增加。\n所以，养成 defer timer.Stop() 的习惯，是一个很小但值得注意的细节。\n总结一下：\nselect 只能监听 channel。 time.NewTimer 返回的 timer 里有一个 C 字段，它就是 channel。 time.After(d) 本质上就是 NewTimer(d).C。 如果 timer 可能提前不再使用，应该及时调用 Stop，避免无意义的定时事件堆积。 ","date":"2026-05-12","externalUrl":null,"permalink":"/posts/time-after-select/","section":"文章","summary":"","title":"time.After 凭什么能被 select 监听？","type":"posts"},{"content":" Huayang Gao University of South China Software Engineering Junior Go backend engineering and AI application practice. I write about projects, source code reading, engineering notes and long-term learning.\nDaily Dev Word\nhandler 处理函数 Go Web A Gin handler receives a request and writes a response.\nNext ","externalUrl":null,"permalink":"/en/","section":"1KURA","summary":"","title":"1KURA","type":"page"},{"content":"I am Huayang Gao, a junior software engineering student at the University of South China, focusing on Go backend development.\nThis blog records three types of notes:\nProject reviews: explaining backend and AI application projects clearly. Source code reading: writing down the actual execution paths and details I understand. Engineering notes: organizing Go, Redis, RabbitMQ, MySQL, Docker, RAG and related topics. GitHub: 1KURA-hub\n","externalUrl":null,"permalink":"/en/about/","section":"1KURA","summary":"","title":"About","type":"page"},{"content":"","externalUrl":null,"permalink":"/en/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"","externalUrl":null,"permalink":"/en/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","externalUrl":null,"permalink":"/en/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":" Projects # This page collects the backend and AI application projects I maintain and review.\nHigh-Concurrency Course Selection System # A course selection system built around Redis Lua pre-deduction, Redis Stream, RabbitMQ async writes, idempotent consumption and dead letter compensation.\nGitHub: course-select GitHub Code Repository RAG Assistant # A RAG application that imports GitHub repositories, chunks code, performs vector retrieval, answers code questions and analyzes Git diff impact.\nGitHub: code-rag-assistant ","externalUrl":null,"permalink":"/en/projects/","section":"Projects","summary":"","title":"Projects","type":"projects"},{"content":"","externalUrl":null,"permalink":"/en/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","externalUrl":null,"permalink":"/en/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"}]