在Go语言中,有一种特殊的通道类型叫做Buffered Channel(缓冲通道),它在通道中储存一定数量的元素。当通道中的元素数量达到设定的上限时,写入操作会被阻塞住,直到有其他协程从通道中读取元素。相反,当通道中的元素数量为零时,读取操作也会被阻塞住,直到有其他协程向通道中写入元素。这种阻塞机制可以有效地控制协程之间的同步和通信。在本文中,我们将详细介绍Go语言中Buffered Channel的阻塞机制。
在《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
此行为与此练习的描述不同,其中指出:
<code> Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty </code>
在 ch <- 2
行之后,ch
is 已满,并且由于我们只运行 1 个单独的 Goroutine,即主 Goroutine,因此该 Goroutine 应该被阻塞,直到 ch
is 被接收者消耗,因此代码不应该到达fmt.Println(<-ch)
行,但应该说类似
<code> fatal error: all goroutines are asleep - deadlock! </code>
但是,由于情况并非如此,我很困惑,并寻求指导。
这是我写的另一段代码
chh := make(chan int, 2) go func() { chh <- 1 fmt.Printf("chh after 1: %v, %v\n", cap(chh), len(chh)) chh <- 2 fmt.Printf("chh after 2: %v, %v\n", cap(chh), len(chh)) chh <- 3 fmt.Printf("chh after 3: %v, %v\n", 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
。
该通道可容纳两人。两次发送可以成功而不会阻塞。 第三个不能。仅当通道在发送之前已满时,发送才会阻塞。
以上是Go 的 Buffered Channel 的阻塞机制的详细内容。更多信息请关注PHP中文网其他相关文章!