如果我定义了一个不带缓冲区的通道并向其中写入一个数据,它会立即阻塞吗(以便内核将寻找从该通道读取的另一个不受阻塞的goroutine),或者它会继续执行并在下次出现时阻塞尚未读取的某些代码会尝试再次写入该通道?
下面是我编写的用于研究此问题的两段代码。
代码1:
package main
import "fmt"
func main() {
c := make(chan int)
go func() {
for i := 0;i < 3; i++ {
c <- i
fmt.Printf("number %v inserted into channel\n", i)
}
}()
for i := 0; i < 3; i++ {
fmt.Printf("number poped from channel %v\n", <-c)
}
}
输出如下:
number 0 inserted into channel
number poped from channel 0
number poped from channel 1
number 1 inserted into channel
number 2 inserted into channel
number poped from channel 2
第一次写入c后,似乎已继续执行该goroutine,因为已打印“将0插入通道”。
code2:
package main
import "fmt"
func main() {
c := make(chan int)
c <- 2
fmt.Println("Something is written into channel")
<-c
}
由于在运行时报告了死锁错误,因此这段代码无法正确运行。
fatal error: all goroutines are asleep - deadlock!
在我看来,执行c <-2时,goroutine被阻止(如果未阻止,则将执行fmt.Println行,并且继续执行将在<-c处解锁c)。当此goroutine被阻止时,内核会搜索其他goroutine,但均未找到,因此会报告死锁错误。
总而言之,在第一段代码中,我得出结论,写到通道不会立即阻止goroutine,但是从第二段代码中会阻止。我在哪里弄错了,什么时候通道会阻止goroutine?
答案 0 :(得分:5)
发送到没有可用缓冲区空间的通道会阻塞发送方,直到发送完成为止;从没有可用消息的信道接收将阻塞接收器,直到接收完成。未缓冲的通道永远不会有缓冲空间-发送块直到有东西接收为止,反之亦然。此内容已包含在“漫游之旅”中:https://tour.golang.org/concurrency/2
请记住,您的代码是并发的,因此您不能过多地理解输出语句的顺序。在发送/接收操作和将消息打印到标准输出之间时,可以使goroutine处于睡眠状态。