Go语言程序永久阻塞疑问:time.Tick与通道遍历相关咨询
两段Go代码的阻塞/死锁原因分析及疑问确认
第一段永久阻塞的代码
package main import ( "fmt" "time" ) func main() { burstyLimiter := make(chan time.Time, 3) for i := 1; i <= 3; i++ { burstyLimiter <- time.Now() } go func() { for t := range time.Tick(200 * time.Millisecond) { burstyLimiter <- t } }() burstyRequests := make(chan int, 5) for i := 1; i <= 5; i++ { burstyRequests <- i } for r := range burstyRequests { fmt.Println("received request", r, time.Now()) } }
第二段死锁的代码
package main import ( "fmt" "time" ) func main() { burstyRequests := make(chan int, 5) for i := 1; i <= 5; i++ { burstyRequests <- i } for r := range burstyRequests { fmt.Println("received request", r, time.Now()) } }
你的疑问确认与原因拆解
你的猜测部分正确,但需要明确两个问题的边界:
goroutine泄漏确实存在,但不是main goroutine永久阻塞的直接原因
后台启动的goroutine通过time.Tick持续向burstyLimiter写入数据,而main函数全程没有读取burstyLimiter的逻辑。由于burstyLimiter是容量为3的缓冲通道,当缓冲被填满后,这个后台goroutine会永久阻塞在burstyLimiter <- t这一行,形成goroutine泄漏,但这个泄漏不会直接导致main goroutine阻塞。main goroutine永久阻塞的真正原因:未关闭通道导致for range遍历持续等待
两段代码的核心问题都是burstyRequests通道没有被关闭。Go中用for range遍历通道时,只有当通道被显式关闭且缓冲内的元素全部被读取完毕后,遍历才会退出:- 第二段代码里,没有其他goroutine会向
burstyRequests写入数据,main goroutine读完缓冲的5个元素后就会一直等待新数据,没有退出条件,最终触发死锁; - 第一段代码里,main goroutine的阻塞原因和第二段完全一致:
burstyRequests未关闭,遍历读完5个元素后持续等待,导致永久阻塞。
- 第二段代码里,没有其他goroutine会向
简单来说,你提到的goroutine泄漏是第一段代码的额外问题,但不是程序永久阻塞的核心原因;而"通道遍历因未关闭、持续等待新数据而阻塞"才是两段代码出问题的关键。
内容的提问来源于stack exchange,提问作者opensaucerer
相关产品推荐
相关产品推荐

