单个`select`情况下的链接通道操作

YUT*_*LAI 3 go

我尝试复用2个通道A和B. A发送延迟10毫秒,B发送1秒.我使用select等待A和B,并将结果发送到扇入通道,然后在main中接收值.

package main

import  (
"fmt"
"time"
)

func talk(msg string, wait_time int) <-chan string {
    ch := make(chan string)
    go func () {
        for i:=0;i<5;i++ {
            ch <- fmt.Sprintf("%s %d", msg, i)
            time.Sleep(time.Duration(wait_time)*time.Millisecond)
        }
    }()

    return ch
}

func fanIn(input1, input2 <-chan string) <-chan string {
    ch := make(chan string)
    go func () {
        for {
            select {
                case t :=<-input1:
                    ch <- t
                case t := <-input2:
                    ch <- t
            }
        }
    }()

    return ch
}
func main() {
    ch := fanIn(talk("A", 10), talk("B", 1000))

    for i:=0; i<10; i++ {
        fmt.Printf("%q\n", <-ch)
    }
    fmt.Printf("Done\n")

}
Run Code Online (Sandbox Code Playgroud)

这将得到正确的结果,如下所示

"A 0"
"B 0"
"A 1"
"A 2"
"A 3"
"A 4"
"B 1"
"B 2"
"B 3"
"B 4"
Done
Run Code Online (Sandbox Code Playgroud)

我的问题是,当我改变case语句时,我得到了奇怪的输出.似乎有些价值被取消了,当然没有更多的价值收到粉丝通道并且发生了死锁.

select {
                case ch<- <-input1:
                case ch<- <-input2:
            }
Run Code Online (Sandbox Code Playgroud)

结果是这样的:

"B 0"
"A 1"
"B 2"
"A 3"
"A 4"
fatal error: all goroutines are asleep - deadlock!
Run Code Online (Sandbox Code Playgroud)

有谁知道这种情况?

zer*_*kms 6

这是因为在select中只有一个通道读或写是非阻塞的.

所有其他操作都正常运行.

所以在这段代码中

select {
                case ch<- <-input1:
                case ch<- <-input2:
            }
Run Code Online (Sandbox Code Playgroud)

它从input1(阻塞)接收一个值.它等待延迟并收到A 0.

它试图写它ch,非阻塞.

如果代码中的代码main足够快到达

 fmt.Printf("%q\n", <-ch)
Run Code Online (Sandbox Code Playgroud)

然后写入一个频道成功.

然后fanIn for循环开始第二次迭代:这次选择第二case次(它不是确定性的).在这一点上,第二个goroutine可能已经写了这个B 0值.

但是有可能main函数循环没有消耗组合通道的值.

所以价值下降了.

这会重复多次,你会失去很少的价值,最终没有作家和读者永远等待剩下的价值.

这个稍微修改过的代码副本演示了它:https://play.golang.org/p/lcM5OKx09Dj

  • 对于OP:通常,请阅读[规范](https://golang.org/ref/spec#Select_statements),其中说明:«对于语句中的所有情况,接收操作的通道操作数和通道以及发送语句的右侧表达式在输入"select"语句后按源顺序精确评估一次.<...>无论选择哪种(如果有的话)通讯操作,都会发生评估中的任何副作用.» (3认同)