我尝试多路复用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
我的问题是,当我更改案例陈述时,我得到了奇怪的输出。似乎有些值下降了,当然在扇入通道中没有收到更多的值并且发生了死锁。
select {
case ch<- <-input1:
case ch<- <-input2:
}
结果是这样的:
"B 0"
"A 1"
"B 2"
"A 3"
"A 4"
fatal error: all goroutines are asleep - deadlock!
有人知道这种情况吗?
答案 0 :(得分:6)
之所以发生这种情况,是因为在选择中,只有一个通道的读取或写入是非阻塞的。
所有其他操作均正常。
所以在这段代码中
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
答案 1 :(得分:2)
我将解释为什么您在渠道中的价值被降低了。 基于Golang文档中有关执行select语句的事实:
对于该语句中的所有情况,输入“ select”语句后,将按源顺序对接收操作的通道操作数以及send语句的通道表达式和右侧表达式进行一次精确评估。结果是一组要从中接收或发送到的通道,以及要发送的相应值。不管选择进行哪个通信操作,都会发生该评估中的任何副作用。具有简短变量声明或赋值的RecvStmt左侧的表达式尚未评估。
结果是一组接收或发送到的通道,因此,当您在左侧传递通道时,请先使用select语句尝试获取 ALL 右侧语句在尝试发送到左侧语句中的通道之前先进行评估
很显然,必须在一轮选择语句中接收到input1和input2,然后将其发送到ch,因此ch通道始终具有正好5的值,该值与intput1和input2混合在一起 ,尽管延迟了
在此代码中向input1和input2通道发送值时,您可以看到所有数据已打印: Link
答案 2 :(得分:1)
(我读过这篇文章,我还没有在GO文档中看到这种'case'子句的特定组合的具体解释)。
当您的“选择”中包含涉及多个渠道的“案例”子句时,GO会选择其中的任何个,并将其作为“唤醒选择的渠道”
在您的第一个代码段中,“ case”子句仅引用input1和input2(正确的-您实际想要的内容,即“等待input1或input2”)。
在第二个代码段中,“ case”子句中同时包含三个(import xml.etree.ElementTree as ET
root = ET.fromstring('<parent><child></child></parent>')
child = root.find('child')
parent = child.find('..') # retrieve the parent
parent is None # unexpected answer True
,input1
和input2
),因此该语句显示为“等待ch
或input1
可读或input2
可写”。现在,ch
可以由于主要等待而变得易于写入,因此您的“选择”可以在那醒来,并最终对input1或intput2进行读取,而此时它们却无法被读取(不是您想要的内容) )。