为了账号安全,请及时绑定邮箱和手机立即绑定

“消费或放回”转到频道

“消费或放回”转到频道

Go
幕布斯7119047 2021-12-06 16:35:44
我试图有两个单独的消费者 go 例程,它会从输入通道中过滤掉偶数和奇数。这只是一个玩具示例,目的是查看是否有可能让消费者对从输入通道读取的消息在匹配特定条件时执行某些操作,否则将其放回输入通道。我目前的代码如下:package mainfunc filterOdd(ch chan int, out chan int) {    val := <- ch    if val % 2 == 0 {        ch <- val    } else {        out <- val    }}func filterEven(ch chan int, out chan int) {    val := <- ch    if val % 2 != 0 {        ch <- val    } else {        out <- val    }}func main() {    even := make(chan int)    odd := make(chan int)    input := make(chan int)    go filterOdd(input, odd)    go filterEven(input, even)    for i:=1; i <= 10; i++ {        input <- i    }    println("Even...")    for i := range even {        println(i)    }    println("Odd...")    for i := range odd {        println(i)    }}但是,这会产生以下输出:fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:main.main()    /tmp/sandbox594577124/main.go:27 +0x140goroutine 4 [chan send]:main.filterOdd(0x10336100, 0x103360c0)    /tmp/sandbox594577124/main.go:8 +0xc0created by main.main    /tmp/sandbox594577124/main.go:24 +0xc0Go Playground 链接:https : //play.golang.org/p/9RIvFsGKI-
查看完整描述

1 回答

?
森林海

TA贡献2011条经验 获得超2个赞

你有一个死锁,因为你的偶数和奇数 goroutines 在发送到时被阻止,out因为没有从中读取任何内容。为什么什么都不读out?因为maingoroutine 在发送到时被阻止,input因为没有从中读取任何内容。为什么没有读取input?因为从中读取的两个 goroutine 被阻塞了。

此外,除非您将它们的内容包装在其中,否则它们filterEvenfilterOdd都只会运行一次for { }(但是在您之前它们永远不会停止break)。另一方面,当没有任何东西可以写入时,range even将阻塞(并且range odd永远不会发生)even,因为range只有当通道关闭或被break调用时,通道才会停止。

一般来说,只要您知道何时可以关闭通道,这些都不是很难解决的问题。根据您的描述,这变得更加困难。没有一个 goroutine 知道什么时候可以关闭input,因为所有三个都向它写入,两个也从它读取。您可以使用 async.WaitGroup来确保input在关闭之前已处理您放入通道的所有内容。一旦它关闭,另外两个 goroutine 就可以使用它作为关闭它们自己的通道breakreturn完成运行的信号。

但是,对inout通道的写入仍然会阻塞,直到有相应的读取,因为它们是无缓冲的。但是,如果您通过将大小指定为 的第二个参数来缓冲它们,则make在通道已满之前不会阻止写入。既然你既不知道evenodd将有更多的写入他们比什么maininput,你可以使用它作为安全缓冲能力。

这是WaitGroup为您的代码使用带缓冲通道的示例:https : //play.golang.org/p/VXqfwUwRcx

如果您不想要缓冲通道,您还可以使用另一对 goroutines 来捕获值并main在完成后将它们作为切片发送回。这种方式写在evenodd渠道不会阻塞:https://play.golang.org/p/i5vLDcsK1v

否则,如果不需要一次打印每个频道的内容,您可以使用这两个额外的 goroutine 从频道中读取并立即打印:https : //play.golang.org/p/OCaUTcJkKB


查看完整回答
反对 回复 2021-12-06
  • 1 回答
  • 0 关注
  • 129 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信