在《Tour of Go》中,示例代码是这样给出的:
package main import "fmt" func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println(<-ch) fmt.Println(<-ch) }
它执行良好并打印出来
1 2
此行为与此练习的描述不同,其中指出:
Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty
在 ch <- 2
行之后,ch
is 已满,并且由于我们只运行 1 个单独的 Goroutine,即主 Goroutine,因此该 Goroutine 应该被阻塞,直到 ch
is 被接收者消耗,因此代码不应该到达fmt.Println(<-ch)
行,但应该说类似
fatal error: all goroutines are asleep - deadlock!
但是,由于情况并非如此,我很困惑,并寻求指导。
这是我写的另一段代码
chh := make(chan int, 2) go func() { chh <- 1 fmt.Printf("chh after 1: %v, %vn", cap(chh), len(chh)) chh <- 2 fmt.Printf("chh after 2: %v, %vn", cap(chh), len(chh)) chh <- 3 fmt.Printf("chh after 3: %v, %vn", cap(chh), len(chh)) }() fmt.Println(<-chh) fmt.Println(<-chh) fmt.Println(<-chh)
执行结果为
1 chh after 1: 2, 0 chh after 2: 2, 0 chh after 3: 2, 1 2 3
这更令人困惑。这次有另一个 goroutine 进行发送。我的期望是,在第一个 fmt.Println(<-chh)
期间,主 goroutine 应该被阻塞。调度程序将选择运行匿名函数的 goroutine,并且它应该执行到 chh <- 2
,然后它会阻塞自身,调度程序再次恢复到主 goroutine。然而,如结果所示,第二个 goroutine 在 chh <- 1
之后立即被阻塞。为什么会这样?
编辑: 我仍然不明白为什么我的本地首先打印 1 。当我在远程服务器上尝试使用 go Playground 时,它显示出不同的行为,现在与我的期望一致。
已知channel是由3个队列组成(接收goroutines、发送goroutines ans value buffer),当匿名函数运行时,channel chh
的状态为(sending:empty,valuebuffer:empty,receiving:[main] )
。
正在运行的子 Goroutine 只是将值直接推入主 Goroutine,而没有实际将其传递到值缓冲区。这就是为什么chh
推送后1
的长度是0
。
该通道可容纳两人。两次发送可以成功而不会阻塞。 第三个不能。仅当通道在发送之前已满时,发送才会阻塞。