为了账号安全,请及时绑定邮箱和手机立即绑定

Go 中的通道阻塞是如何工作的?

Go 中的通道阻塞是如何工作的?

Go
江户川乱折腾 2021-11-08 14:47:28
我正在学习 Go 语言。这是我遇到的一个例子。有人可以解释一下这里发生了什么吗?package mainimport "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")      }    }}输出:DefaultDefaultProgram Exited如果我注释掉默认部分//default://    fmt.Println("Default")输出变为:received onereceived twoProgram exited.default案例的存在如何改变通道阻塞的工作方式?
查看完整描述

2 回答

?
慕的地6264312

TA贡献1817条经验 获得超6个赞

这与select语句在 Go 中的工作方式有关。

Go 文档中select

如果可以进行一个或多个通信,则通过统一伪随机选择选择可以进行的单个通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,“select”语句会阻塞,直到至少有一个通信可以继续。

因此,如果没有默认情况,代码将一直阻塞,直到任一通道中的某些数据可用。它隐式地等待其他 goroutines 唤醒并写入它们的通道。

当您添加 default case 时,很可能select在其他 goroutines 从睡眠中醒来之前到达该语句。

因此,由于(尚)没有可用数据,并且存在默认情况,因此会执行默认情况。此操作进行两次,耗时不到 1 秒。所以程序在任何 go 例程有机会唤醒并写入通道之前终止。

请注意,这在技术上是一种竞争条件;绝对不能保证循环的 2 次迭代会在任何 go 例程唤醒之前运行,因此理论上即使在默认情况下也可能有不同的输出,但实际上这是极不可能的。


查看完整回答
反对 回复 2021-11-08
?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

select语句会阻塞,直到至少一个 case 准备就绪。Go 语言规范部分内容如下:

如果可以进行一个或多个通信,则通过统一伪随机选择选择可以进行的单个通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,“select”语句会阻塞,直到至少有一个通信可以继续。

在原始代码中,default案例在循环的两次迭代中都已准备就绪,因为在c1或上发送任何内容之前存在延迟c2

删除default案例后,select语句必须等待数据在c1或 中可用c2


查看完整回答
反对 回复 2021-11-08
  • 2 回答
  • 0 关注
  • 284 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信