我有一段代码,我试图理解基于我如何把close
调用和位置
func main() {
ch := make(chan int, 2)
go func(ch chan int) {
for i := 1; i <= 5; i++ {
ch <- i
fmt.Println("Func goroutine sends data: ", i)
}
//Pos1 - Works perfectly
//close(ch)
}(ch)
fmt.Println("Main goroutine sleeps 2 seconds")
time.Sleep(time.Second * 2)
fmt.Println("Main goroutine begins receiving data")
//Pos2 - Puts in only 2 ints 1 and 2 and then prints only that
//close(ch)
for d := range ch {
fmt.Println("Main goroutine received data:", d)
}
//Pos3 - Throws fatal error
close(ch)
}
我一直在努力理解和阅读博客,但仍然无法理解某些事情
close
在这个位置工作?for loop
在尝试将更多元素写入已关闭的通道时不会抛出异常?fatal error: all goroutines are asleep - deadlock!
虽然打印了所有5个整数。这是为什么?但是我想在缓冲通道上做一个范围,范围函数必须事先知道要迭代多少个元素并且必须关闭该通道。
这种假设是错误的,是所有误解的根源。
Go Spec:https://golang.org/ref/spec#For_statements中描述了通道上测距的行为
对于通道,产生的迭代值是在通道上发送的连续值,直到通道关闭。如果通道为nil,则范围表达式将永久阻塞。
在评估for语句并且语句不需要知道元素数量时,不需要关闭通道。
所以,在你的代码中,当你将close
放在Pos1中时,它确实是正确的方法。当你把它放在Pos3中时,for循环等待关闭的通道,这只能在for循环本身之后发生,所以它是一个死锁。
将close
放在Pos2中是有缺陷的,行为有点棘手。可能会引发错误,但可以输出两个数字。这是因为当在for循环之前关闭通道时,循环可以无阻塞地运行,然后main()
返回。当main()
返回时,Go程序结束。是否引发错误仅取决于调度程序是否在进程之间切换到goroutine,这是不能保证的。