卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章64334本站已运行4115

Go 的 Buffered Channel 的阻塞机制

go 的 buffered channel 的阻塞机制

在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

此行为与此练习的描述不同,其中指出:


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( 期间,主 goroutine 应该被阻塞。调度程序将选择运行匿名函数的 goroutine,并且它应该执行到 chh ,然后它会阻塞自身,调度程序再次恢复到主 goroutine。然而,如结果所示,第二个 goroutine 在 chh 之后立即被阻塞。为什么会这样?

编辑: 我仍然不明白为什么我的本地首先打印 1 。当我在远程服务器上尝试使用 go Playground 时,它显示出不同的行为,现在与我的期望一致。

已知channel是由3个队列组成(接收goroutines、发送goroutines ans value buffer),当匿名函数运行时,channel chh的状态为(sending:empty,valuebuffer:empty,receiving:[main] )

正在运行的子 Goroutine 只是将值直接推入主 Goroutine,而没有实际将其传递到值缓冲区。这就是为什么chh推送后1的长度是0

解决方法

该通道可容纳两人。两次发送可以成功而不会阻塞。 第三个​​不能。仅当通道在发送之前已满时,发送才会阻塞。

卓越飞翔博客
上一篇: 删除无法运行 kubernetes 操作员控制器的指标
下一篇: Go 冒号等于运算符和一个新变量
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏