尝试实现死锁时使用零的无限循环
Infinite loop with zeros while trying to achieve deadlock
下面的代码一直打印0
.
package main
import (
"fmt"
)
func main() {
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i // INPUT
}
close(c)
}()
for {
fmt.Print(<-c) // RECEIVER; OUTPUT
}
}
根据我的理解,它应该打印 0
到 9
然后继续无限循环。为什么它一直打印 zero
?
你的理解(几乎)是正确的,只是主goroutine中的无限循环不会阻塞而是会不停地接收和打印。
您启动的 goroutine 在通道上发送数字 0、1、... 9,然后关闭它。并且从关闭的通道接收不会阻塞,相反,它可以立即进行,并且它产生通道的元素类型的零值,对于类型 int
是 0
。这在 Spec: Receive operator:
中说明
Receiving from a nil
channel blocks forever. A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.
所以你看到了你应该看到的。首先它打印数字 0..9
,然后非常快速地打印 0
(没有任何延迟),所以您可能甚至没有注意到初始的 0..9
数字。
稍微修改您的示例,使主 goroutine 中的循环在 15 次迭代后退出,将立即显示发生的情况:
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i // INPUT
}
close(c)
}()
for i := 0; i < 15; i++ {
fmt.Print(<-c) // RECEIVER; OUTPUT
}
输出(在 Go Playground 上尝试):
012345678900000
如果您的目标是在处理(打印)完所有发送的号码后退出,请在频道上使用 for range
:
for i := range c {
fmt.Print(i) // RECEIVER; OUTPUT
}
如果您的目标是在处理完这 10 个号码后一直阻止直到有新号码可用,那么请不要关闭频道。这样下一个接收操作就会阻塞。在这种情况下,您的原始循环和 for range
循环都可以工作,但 for range
更好,因为如果 / 当通道关闭时它总是退出。
看看这道题,记住通道公理:
下面的代码一直打印0
.
package main
import (
"fmt"
)
func main() {
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i // INPUT
}
close(c)
}()
for {
fmt.Print(<-c) // RECEIVER; OUTPUT
}
}
根据我的理解,它应该打印 0
到 9
然后继续无限循环。为什么它一直打印 zero
?
你的理解(几乎)是正确的,只是主goroutine中的无限循环不会阻塞而是会不停地接收和打印。
您启动的 goroutine 在通道上发送数字 0、1、... 9,然后关闭它。并且从关闭的通道接收不会阻塞,相反,它可以立即进行,并且它产生通道的元素类型的零值,对于类型 int
是 0
。这在 Spec: Receive operator:
Receiving from a
nil
channel blocks forever. A receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received.
所以你看到了你应该看到的。首先它打印数字 0..9
,然后非常快速地打印 0
(没有任何延迟),所以您可能甚至没有注意到初始的 0..9
数字。
稍微修改您的示例,使主 goroutine 中的循环在 15 次迭代后退出,将立即显示发生的情况:
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
c <- i // INPUT
}
close(c)
}()
for i := 0; i < 15; i++ {
fmt.Print(<-c) // RECEIVER; OUTPUT
}
输出(在 Go Playground 上尝试):
012345678900000
如果您的目标是在处理(打印)完所有发送的号码后退出,请在频道上使用 for range
:
for i := range c {
fmt.Print(i) // RECEIVER; OUTPUT
}
如果您的目标是在处理完这 10 个号码后一直阻止直到有新号码可用,那么请不要关闭频道。这样下一个接收操作就会阻塞。在这种情况下,您的原始循环和 for range
循环都可以工作,但 for range
更好,因为如果 / 当通道关闭时它总是退出。
看看这道题,记住通道公理: