为何Go语言Channel未取到最后一次写入的值?
Go Channel读取结果异常原因分析
问题场景
你在学习Go Channel时遇到了一个奇怪的现象:向带缓冲容量3的Channel写入1到10后,关闭Channel,最后在main函数中额外读取一次Channel,得到的总是倒数第二个写入的值(比如写入10时得到8,写入8时得到6)。你的代码如下:
func main() { channel := make(chan int, 3) isOver := make(chan bool) go func() { for val := range channel { fmt.Println(val) } isOver <- true }() channel <- 1 channel <- 2 channel <- 3 channel <- 4 channel <- 5 channel <- 6 channel <- 7 channel <- 8 channel <- 9 channel <- 10 close(channel) fmt.Println("Channel Value is: ", <-channel) <-isOver }
原因解释
核心问题出在main函数的额外读取操作和带缓冲Channel的阻塞特性上,具体流程如下:
- 你的Channel缓冲容量为3,main函数顺序写入值时,每当缓冲被占满,写入操作会阻塞,直到后台goroutine读取一个值释放出缓冲空间,main才能继续写入下一个值。
- 当main写完最后一个值(比如10)时,Channel缓冲里恰好剩下3个未被goroutine读取的值:
[8,9,10]。 - main关闭Channel后,立刻执行了
<-channel操作,抢先读取了缓冲里的第一个值(8)。 - 之后后台goroutine才继续读取剩下的9和10并打印,因此你会看到goroutine的输出里没有8,而main的打印结果是8。
当你把最后一次写入的值改成8时,写完后缓冲里剩下的是[6,7,8],main抢先读取第一个值6,goroutine后续读取7和8,所以结果变成6,逻辑完全一致。
解决建议
如果希望goroutine读取所有写入Channel的值,只需要移除main函数中那行额外的<-channel读取操作即可:
// 删掉这一行 // fmt.Println("Channel Value is: ", <-channel)
内容的提问来源于stack exchange,提问作者KaganBerk
相关产品推荐
相关产品推荐

