猿问

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

我正在学习 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案例的存在如何改变通道阻塞的工作方式?


江户川乱折腾
浏览 285回答 2
2回答

慕的地6264312

这与select语句在 Go 中的工作方式有关。从Go 文档中select:如果可以进行一个或多个通信,则通过统一伪随机选择选择可以进行的单个通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,“select”语句会阻塞,直到至少有一个通信可以继续。因此,如果没有默认情况,代码将一直阻塞,直到任一通道中的某些数据可用。它隐式地等待其他 goroutines 唤醒并写入它们的通道。当您添加 default case 时,很可能select在其他 goroutines 从睡眠中醒来之前到达该语句。因此,由于(尚)没有可用数据,并且存在默认情况,因此会执行默认情况。此操作进行两次,耗时不到 1 秒。所以程序在任何 go 例程有机会唤醒并写入通道之前终止。请注意,这在技术上是一种竞争条件;绝对不能保证循环的 2 次迭代会在任何 go 例程唤醒之前运行,因此理论上即使在默认情况下也可能有不同的输出,但实际上这是极不可能的。

拉莫斯之舞

该select语句会阻塞,直到至少一个 case 准备就绪。Go 语言规范部分内容如下:如果可以进行一个或多个通信,则通过统一伪随机选择选择可以进行的单个通信。否则,如果存在默认情况,则选择该情况。如果没有默认情况,“select”语句会阻塞,直到至少有一个通信可以继续。在原始代码中,default案例在循环的两次迭代中都已准备就绪,因为在c1或上发送任何内容之前存在延迟c2。删除default案例后,select语句必须等待数据在c1或 中可用c2。
随时随地看视频慕课网APP

相关分类

Go
我要回答