从缓冲通道和非缓冲通道中进行选择
Posted
技术标签:
【中文标题】从缓冲通道和非缓冲通道中进行选择【英文标题】:Selecting from buffered vs non-buffered channel 【发布时间】:2021-10-13 22:55:02 【问题描述】:当我运行以下简单代码时,它会按预期打印“ping”:
messages := make(chan string)
go func() messages <- "ping" ()
fmt.Println(<-messages)
但是,当我使用与 select 相同的非缓冲通道时,它不会通过 ping 来完成它,因此会打印“未发送消息”:
messages := make(chan string)
select
case messages <- "ping":
fmt.Println("sent message")
default:
fmt.Println("no message sent")
为什么会这样?通道是相同的,但是可以通过 goroutine 访问,但不能通过 select 访问。
此外,我发现当我将其转换为缓冲通道(大小为 1)时,它会像魅力一样完成通道:为什么?
messages := make(chan string,1)
select
case messages <- "ping":
fmt.Println("sent message")
default:
fmt.Println("no message sent")
请注意,没有任何东西挂断,而是立即返回,正如我所描述的那样。
【问题讨论】:
这就是缓冲/非缓冲和默认情况下选择的工作原理; case 1/ chan 没有空间接受该值,所以选择跳转到默认值。案例 2/ 通道有 1 个空间来接受 1 个值,因此选择会跳转到该案例。 @mh-cbon 第一个 sn-p 是什么?没有房间但虽然收到的那一个? 你有阅读器,所以它可以工作。不过,该行为与缓冲通道略有不同。下面的答案很好地解释了这一点。你理解它们有困难吗? 【参考方案1】:select
语句阻塞,直到任何一个 case
s 准备好,或者如果没有一个准备好,它运行 default
情况。
在您的第二个程序中,select
之前没有出现接收操作,因此 case messages <- "ping"
还没有准备好 - 没有接收器 - 并且始终执行 default
。
使用缓冲通道,即使另一端没有接收器,发送操作也不会阻塞,因此case messages <- "ping"
已准备好并运行。
在带有 goroutine 的 sn-p 中,发送操作并发运行,因此主程序流程可以继续进行 fmt.Println
调用并阻塞 <-messages
,直到并发发送使通道上的值可用
【讨论】:
【参考方案2】:当你使用 Unbuffered 通道类型时,go 需要某种 “检查点”(阻塞语句或其他 goroutine 中的超时) 来切换正在执行的 goroutine 通道将被阻塞,直到有东西读取它,当主 go-routine 进入 select 语句时,选择阻塞,直到有一些活动(消息)或在其中一种情况下发生释放,并且由于您的案例读取尚未完成, 通道阻塞,因为 无缓冲通道需要读取和写入才能充当检查点,因此选择跳转到默认语句。 当您使用缓冲通道时,任何活动都会通过通道发送信号,但在缓冲区满之前不会被阻塞。因此,当使用缓冲通道时,无需阻塞读取器,因此 select 会在通道上检测到要使用的数据。
也在
messages := make(chan string)
go func() messages <- "ping" ()
fmt.Println(<-messages)
你在频道上等待
没有读取通道保持无缓冲通道,阻塞:
messages := make(chan string)
select
case messages <- "ping":
fmt.Println("sent message")
default:
fmt.Println("no message sent")
缓冲通道不会阻塞,因此将执行写入操作:
messages := make(chan string,1)
select
case messages <- "ping":
fmt.Println("sent message")
default:
fmt.Println("no message sent")
【讨论】:
以上是关于从缓冲通道和非缓冲通道中进行选择的主要内容,如果未能解决你的问题,请参考以下文章