Go 中的通道阻塞是如何工作的?
Posted
技术标签:
【中文标题】Go 中的通道阻塞是如何工作的?【英文标题】:How does channel blocking work in Go? 【发布时间】:2015-12-08 21:34:02 【问题描述】:我正在学习 Go 语言。这是我遇到的一个例子。有人可以解释一下这里发生了什么吗?
package main
import "time"
import "fmt"
func main()
c1 := make(chan string)
c2 := make(chan string)
go func()
time.Sleep(time.Second * 1)
c1 <- "one"
()
go func()
time.Sleep(time.Second * 2)
c2 <- "two"
()
for i := 0; i < 2; i++
select
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
default:
fmt.Println("Default")
输出:
Default
Default
Program Exited
如果我注释掉默认部分
//default:
// fmt.Println("Default")
输出变成:
received one
received two
Program exited.
default
案例的存在如何改变通道阻塞的工作方式?
【问题讨论】:
【参考方案1】:select
语句阻塞,直到至少一个案例准备好。 Go 语言规范reads, in part:
如果一个或多个通信可以继续,则通过统一的伪随机选择选择一个可以继续的通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,“select”语句会阻塞,直到至少有一个通信可以继续。
在原始代码中,default
案例在循环的两次迭代中都准备就绪,因为在 c1
或 c2
发送任何内容之前存在延迟。
删除default
大小写后,select
语句必须等待c1
或c2
中的数据可用。
【讨论】:
感谢您的澄清。 感谢您的支持。我有点恼火,因为我先发布了我的答案。 我选择了另一个答案,因为它包含了程序在 go 例程完成工作之前退出的原因。 我以为我在这里介绍了它:“在原始代码中,default
案例在循环的两次迭代中都已准备就绪,因为在 c1
或 @987654332 上发送任何内容之前存在延迟@。”
@MichaelLaszlo 看到您的评论后,我取消了对已接受答案的投票,并为您投票。【参考方案2】:
这与 select
语句在 Go 中的工作方式有关。
来自the Go documentation on select
:
如果可以进行一项或多项通信,则一项 可以通过统一的伪随机选择来选择。 否则,如果存在默认情况,则选择该情况。如果有 不是默认情况,“select”语句阻塞,直到至少一个 的通信可以继续。
因此,在没有默认情况的情况下,代码将阻塞,直到任一通道中的某些数据可用。它隐式等待其他 goroutine 唤醒并写入它们的通道。
当你添加默认情况时,很可能在其他 goroutine 从睡眠中唤醒之前到达select
语句。
因此,由于(还)没有可用数据,并且存在默认情况,因此执行默认情况。此操作执行两次,所需时间不到 1 秒。所以程序在任何 go 例程有机会唤醒并写入通道之前终止。
请注意,这在技术上是一种竞争条件;绝对不能保证循环的 2 次迭代会在任何 go 例程唤醒之前运行,因此理论上即使在默认情况下也可能有不同的输出,但实际上这极不可能。
【讨论】:
【参考方案3】:https://tour.golang.org/concurrency/5
https://tour.golang.org/concurrency/6
请参阅上面给出的链接以了解示例执行情况。如果没有其他案例准备好,则执行默认案例。 在 golang 块中选择,直到其中一个案例准备好。因此删除默认值使得其他情况的执行成为可能,否则它是在其他情况之前准备好的情况
【讨论】:
链接很有用,但如果您可以发布相关代码,就好像链接死了,您的答案将变得无用,谢谢。【参考方案4】:Explanation:
c1 := make(chan string) // Creates a channel of type string. [Means only
strings can be sent/received on this channel]
go func()
time.Sleep(time.Second * 1)
c1 <- "one"
()
// func() is a goroutine [As go keyword is placed before, if no go keyword
here, then it is a simple function].
time.Sleep(time.Second * 1) // here this func() goroutine will sleep for a
second before passing a value to the channel c1.
c1 <- "one"// value "one" is passed to this channel.
select statement: Here it waits for the goroutines to complete it's task.
Once a goroutine above finishes, it matches it's case and executes the
statements.
【讨论】:
这很没用以上是关于Go 中的通道阻塞是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章