遍历 chan 为 nil 的通道
func main() {
var ch chan int
fmt.Println(ch) // <nil>
// fatal error: all goroutines are asleep - deadlock!
for n := range ch {
fmt.Println(n)
}
fmt.Println("not goes here")
}
代码定义了一个整型通道 ch,在程序中尝试对 ch 进行读取操作,但是没有对其进行初始化,因此其值为 nil。在程序中打印变量 ch 的值,输出结果为 nil。接着,使用 range 关键字遍历通道 ch,尝试从中读取元素,并将读取到的元素赋值给变量 n。由于通道未初始化,没有与之关联的底层数据结构,因此读取操作会立即导致程序陷入死锁状态,无法继续执行后面的代码。因此,最终输出的结果只有 nil。
需要注意的是,因为程序陷入死锁状态,最后一行打印语句 fmt.Println("not goes here") 永远不会执行。
遍历未关闭的 chan 对象
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
/*
1
2
3
*/
for n := range ch {
fmt.Println(n)
}
// fatal error: all goroutines are asleep - deadlock!
fmt.Println("goes here")
}
代码定义了一个整型缓冲通道 ch,容量为 3,并向其中顺序发送三个元素,分别是 1、2 和 3。然后,使用 range 关键字遍历通道 ch,尝试从中读取元素,并将读取到的元素赋值给变量 n。
在循环中,由于通道已经包含了所有元素,并且没有其他的发送操作可以继续往通道中添加新的元素,因此循环会一直阻塞等待新元素的到来。当通道中的所有元素都被读取完毕后,range 循环会自动结束,因此可以看到输出结果分别是 1、2 和 3。
接着,程序会继续执行打印语句 fmt.Println("goes here"),但是因为之前的 range 循环已经将通道中的所有元素都读取完毕,没有其他的接收操作可以继续从通道中读取新的元素,因此程序会再次陷入死锁状态,无法继续执行后面的代码。因此,最终输出的结果只有 1、2、3。
当 chan 关闭后 range 迭代安全结束
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
go func() {
time.Sleep(3 * time.Second)
close(ch)
}()
/*
1
2
3
*/
for n := range ch {
fmt.Println(n)
}
// fatal error: all goroutines are asleep - deadlock!
fmt.Println("goes here")
}
代码定义了一个整型缓冲通道 ch,容量为 3,并向其中顺序发送三个元素,分别是 1、2 和 3。接着,使用一个匿名的无缓冲通道来等待 3 秒钟后关闭缓冲通道 ch,这样可以保证 range 循环不会永久阻塞。
然后,使用 range 关键字遍历通道 ch,尝试从中读取元素,并将读取到的元素赋值给变量 n。由于缓冲通道已经包含了所有元素,并且在 3 秒钟后会被关闭,因此循环不会一直阻塞等待新元素的到来。当通道被关闭后,range 循环会自动结束,因此可以看到输出结果分别是 1、2 和 3。
最后,程序会继续执行打印语句 fmt.Println("goes here"),因为在前面已经关闭了通道,因此不会再次阻塞等待新元素的到来,可以正常执行后面的代码。因此,最终输出的结果是 1、2、3 和 goes here。