【问题标题】:How exactly select statement works in channel?select 语句在通道中究竟是如何工作的?
【发布时间】:2021-04-06 15:46:35
【问题描述】:

一个 select 阻塞直到它的一个 case 可以运行,然后它执行那个 case。

我在测试select案例,结果出乎意料:

func main() {
    channel1 := make(chan string)
    channel2 := make(chan string)
    go func() {
        for i := 0; i < 5; i++ {
            channel1 <- "I'll print every 100ms"
            time.Sleep(time.Millisecond * 100)
        }
    }()
    go func() {
        for i := 0; i < 5; i++ {
            channel2 <- "I'll print every 1s"
            time.Sleep(time.Second * 1)
        }
    }()
    for i := 0; i < 5; i++ {
        select {
        case message1 := <-channel1:
            fmt.Println(message1)
        case message2 := <-channel2:
            fmt.Println(message2)
        }
    }
}

大部分时间它打印1s,这与goroutines完美。但是一旦它在100ms之间打印1s

/*
$ go run .
I'll print every 1s
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
$ go run .
I'll print every 1s
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
$ go run .
I'll print every 1s
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
$ go run .
I'll print every 1s
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
$ go run . # WHY 1s IS EXCECUTED IN BETWEEN?
I'll print every 100ms
I'll print every 1s # HERE?
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
$ go run .
I'll print every 1s
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
$ go run .
I'll print every 1s
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
$ go run .
I'll print every 1s
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
*/

根据select 声明,我预计它会产生结果,因为它首先选择了100ms

/*
# Either
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
I'll print every 1s
# Or
I'll print every 100ms
I'll print every 1s
# But not:
I'll print every 100ms
I'll print every 1s
I'll print every 100ms
I'll print every 100ms
I'll print every 100ms
*/

或者,我误解了select 声明?

【问题讨论】:

  • super's answer 尽其所能。当两种情况都准备好时,将选择其中一种。由于前两个 goroutine 中的 sleep 语句发生在发送之后,因此您不能期望来自 channel2 的接收发生在从 channel1 接收之前。此外,因为您使用的是无缓冲通道,并且接收总数 (5) 与发送总数 (10) 不匹配,所以在 main 终止时,您正在泄漏一到两个 goroutine。在这样一个简单的程序中没什么大不了的,但总的来说值得注意。

标签: go


【解决方案1】:

你在睡觉之前在通道上发送,所以哪个先发送取决于哪个 goroutine 启动和运行速度更快。

如果您有一个已准备好多个案例的选择,则顺序是随机的。

【讨论】:

  • @BhojendraRauniyar 如果订单是随机的,您为什么会期望某个订单?
  • @BhojendraRauniyar:当然,但那又怎样?一个发件人开始发送,另一个发件人开始发送,两个发送都被阻止,您的选择开始,您的选择随机选择1s100ms 频道,您随机阅读其中一条消息,打印它,然后您返回并再次阅读,这会立即解除对其他阅读器的阻塞,因为所有这些都发生在几微秒内。所以1s100ms是随机出现的。
  • @BhojendraRauniyar 那没有任何意义。 select 语句不知道每个通道上将发送多少数据包或发送频率。它怎么知道它在 100 毫秒通道上“完成”的时间?
  • @BhojendraRauniyar 通常你不关心订单。您只想同时处理两个或多个频道。如果你需要事情以某种顺序发生,那么有很多不同的方法可以做到这一点。没有选择、等待组、互斥锁等的通道。
  • @BhojendraRauniyar 通道的主要用例是提供不同 goroutine 之间的通信。所以这是一种同步异步代码的方法。在处理异步代码时,你永远无法确定事情会以什么顺序发生。
猜你喜欢
  • 1970-01-01
  • 2021-04-11
  • 2023-04-01
  • 2011-06-26
  • 2021-08-15
  • 2012-06-08
  • 2011-10-11
  • 2013-07-05
相关资源
最近更新 更多