作者:王友仁国珍_326 | 来源:互联网 | 2022-12-11 18:47
我尝试复用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")
}
这将得到正确的结果,如下所示
"A 0"
"B 0"
"A 1"
"A 2"
"A 3"
"A 4"
"B 1"
"B 2"
"B 3"
"B 4"
Done
我的问题是,当我改变case语句时,我得到了奇怪的输出.似乎有些价值被取消了,当然没有更多的价值收到粉丝通道并且发生了死锁.
select {
case ch<- <-input1:
case ch<- <-input2:
}
结果是这样的:
"B 0"
"A 1"
"B 2"
"A 3"
"A 4"
fatal error: all goroutines are asleep - deadlock!
有谁知道这种情况?
1> zerkms..:
这是因为在select中只有一个通道读或写是非阻塞的.
所有其他操作都正常运行.
所以在这段代码中
select {
case ch<- <-input1:
case ch<- <-input2:
}
它从input1
(阻塞)接收一个值.它等待延迟并收到A 0
.
它试图写它ch
,非阻塞.
如果代码中的代码main
足够快到达
fmt.Printf("%q\n", <-ch)
然后写入一个频道成功.
然后fanIn
for
循环开始第二次迭代:这次选择第二case
次(它不是确定性的).在这一点上,第二个goroutine可能已经写了这个B 0
值.
但是有可能main
函数循环没有消耗组合通道的值.
所以价值下降了.
这会重复多次,你会失去很少的价值,最终没有作家和读者永远等待剩下的价值.
这个稍微修改过的代码副本演示了它:https://play.golang.org/p/lcM5OKx09Dj
对于OP:通常,请阅读[规范](https://golang.org/ref/spec#Select_statements),其中说明:«对于语句中的所有情况,接收操作的通道操作数和通道以及发送语句的右侧表达式在输入"select"语句后按源顺序精确评估一次.<...>无论选择哪种(如果有的话)通讯操作,都会发生评估中的任何副作用.»