猿问

“消费或放回”转到频道

我试图有两个单独的消费者 go 例程,它会从输入通道中过滤掉偶数和奇数。这只是一个玩具示例,目的是查看是否有可能让消费者对从输入通道读取的消息在匹配特定条件时执行某些操作,否则将其放回输入通道。


我目前的代码如下:


package main


func 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 +0x140


goroutine 4 [chan send]:

main.filterOdd(0x10336100, 0x103360c0)

    /tmp/sandbox594577124/main.go:8 +0xc0

created by main.main

    /tmp/sandbox594577124/main.go:24 +0xc0

Go Playground 链接:https : //play.golang.org/p/9RIvFsGKI-


幕布斯7119047
浏览 130回答 1
1回答

森林海

你有一个死锁,因为你的偶数和奇数 goroutines 在发送到时被阻止,out因为没有从中读取任何内容。为什么什么都不读out?因为maingoroutine 在发送到时被阻止,input因为没有从中读取任何内容。为什么没有读取input?因为从中读取的两个 goroutine 被阻塞了。此外,除非您将它们的内容包装在其中,否则它们filterEven和filterOdd都只会运行一次for { }(但是在您之前它们永远不会停止break)。另一方面,当没有任何东西可以写入时,range even将阻塞(并且range odd永远不会发生)even,因为range只有当通道关闭或被break调用时,通道才会停止。一般来说,只要您知道何时可以关闭通道,这些都不是很难解决的问题。根据您的描述,这变得更加困难。没有一个 goroutine 知道什么时候可以关闭input,因为所有三个都向它写入,两个也从它读取。您可以使用 async.WaitGroup来确保input在关闭之前已处理您放入通道的所有内容。一旦它关闭,另外两个 goroutine 就可以使用它作为关闭它们自己的通道break或return完成运行的信号。但是,对in和out通道的写入仍然会阻塞,直到有相应的读取,因为它们是无缓冲的。但是,如果您通过将大小指定为 的第二个参数来缓冲它们,则make在通道已满之前不会阻止写入。既然你既不知道even或odd将有更多的写入他们比什么main送input,你可以使用它作为安全缓冲能力。这是WaitGroup为您的代码使用带缓冲通道的示例:https&nbsp;:&nbsp;//play.golang.org/p/VXqfwUwRcx如果您不想要缓冲通道,您还可以使用另一对 goroutines 来捕获值并main在完成后将它们作为切片发送回。这种方式写在even和odd渠道不会阻塞:https://play.golang.org/p/i5vLDcsK1v否则,如果不需要一次打印每个频道的内容,您可以使用这两个额外的 goroutine 从频道中读取并立即打印:https&nbsp;:&nbsp;//play.golang.org/p/OCaUTcJkKB
随时随地看视频慕课网APP

相关分类

Go
我要回答