Go &引用;“消费或收回”;频道

Go &引用;“消费或收回”;频道,go,channel,Go,Channel,我试图有两个独立的消费者围棋程序,这将过滤掉输入通道中的偶数和奇数。这只是一个玩具示例,目的是看看如果消息符合某些条件,是否可以让消费者对从输入通道读取的消息执行某些操作,否则就放回输入通道 我目前的代码如下: package main func filterOdd(ch chan int, out chan int) { val := <- ch if val % 2 == 0 { ch <- val } else { ou

我试图有两个独立的消费者围棋程序,这将过滤掉输入通道中的偶数和奇数。这只是一个玩具示例,目的是看看如果消息符合某些条件,是否可以让消费者对从输入通道读取的消息执行某些操作,否则就放回输入通道

我目前的代码如下:

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)
    }
}

链接到Go Playerd:

您遇到了死锁,因为您的偶数和奇数goroutine在发送到
out时被阻止,因为没有任何内容从中读取。为什么什么都没有读出来?因为
main
goroutine在发送到
input
时被阻止,因为没有从中读取任何内容。为什么没有从
输入读取任何内容?因为将从中读取的两个goroutine被阻止

另外,
filterEvent
filterOdd
将只运行一次,除非您将它们的内容包装在{}
中(但在您
中断之前,它们将永远不会停止)。另一方面,
范围偶数
将在没有剩余内容写入
偶数
时阻塞(并且
范围奇数
永远不会发生),因为通道上的
范围
仅在通道关闭或调用
中断
时停止

一般来说,只要知道何时可以关闭频道,这些问题就不难解决。根据你所描述的,这会变得更加困难。没有一个goroutine知道什么时候可以关闭
输入
,因为三个都向它写入,两个也从中读取。在关闭
输入
频道之前,您可以使用来确保您输入的所有内容都已处理完毕。一旦关闭,其他两个goroutine可以将其作为关闭自己频道的信号,并
中断
返回
以完成运行

但是,写入
in
out
通道仍将阻塞,直到有相应的读取,因为它们是无缓冲的。但是,如果通过指定一个大小作为
make
的第二个参数来缓冲它们,写入操作将不会阻塞,直到通道已满。由于您知道
偶数
奇数
写入的内容都不会比
main
发送到
输入
的内容多,因此可以将其用作安全缓冲容量

下面是一个使用带有缓冲通道的
WaitGroup
的示例:

如果不需要缓冲通道,还可以使用另一对goroutine捕获值,并在完成后将其作为切片发送回
main
。这样,在
偶数
奇数
通道上的写入不会阻塞:


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

@ZanLynx:这个问题说他们的示例代码中出现了死锁,这听起来不像是他们在试图做与另一个问题相同的事情。@JamesHenstridge当然不是同样的事情。但对于这个问题,一般的问题和解决方案似乎是相同的。我刚刚注意到,即使您修复了缓冲区,for循环也不在goroutines中,因此会阻塞,除非您总是使缓冲区大于循环大小。
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