"Consume or put back" 去频道
"Consume or put back" Go channels
我正在尝试有两个单独的消费者 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
Link 去围棋游乐场:https://play.golang.org/p/9RIvFsGKI-
你有一个死锁,因为你的偶数和奇数 goroutines 在发送到 out
时被阻止,因为没有从它读取任何东西。为什么没有读取 out
?因为 main
goroutine 在发送到 input
时被阻塞,因为没有任何东西从它读取。为什么 input
没有任何内容?因为从中读取的两个 goroutines 被阻塞了。
此外,filterEven
和 filterOdd
都只会 运行 一次,除非你将它们的内容包装在 for { }
中(但是它们永远不会停止,直到你 break
).另一方面,range even
将在没有任何内容可写入 even
时阻塞(并且 range odd
永远不会发生),因为通道上的 range
仅在通道为关闭或调用 break
。
总的来说,只要知道什么时候可以关闭频道,这些都不是很难解决的问题。根据您的描述,这变得更加困难。 None 的 goroutines 知道什么时候可以关闭 input
,因为所有三个都写入它,两个也从中读取。您可以使用 sync.WaitGroup
来确保您放入 input
通道的所有内容都已在关闭之前得到处理。一旦关闭,其他两个 goroutine 可以使用它作为关闭自己通道的信号,并 break
或 return
完成 运行ning.
但是,写入 in
和 out
通道仍然会阻塞,直到有相应的读取,因为它们是无缓冲的。但是,如果您通过将大小指定为 make
的第二个参数来缓冲它们,写入将不会阻塞,直到通道已满。由于您知道 even
或 odd
都不会比 main
发送给 input
的内容更多,因此您可以将其用作安全缓冲区容量。
下面是使用带缓冲通道的 WaitGroup
代码的示例:https://play.golang.org/p/VXqfwUwRcx
如果您不需要缓冲通道,您还可以使用另一对 goroutine 来捕获值并在完成后将它们作为切片发送回 main
。这种方式写入 even
和 odd
通道不会阻塞:https://play.golang.org/p/i5vLDcsK1v
否则,如果不需要一次打印每个通道的内容,您可以使用这两个额外的 goroutines 从通道读取并立即打印:https://play.golang.org/p/OCaUTcJkKB
我正在尝试有两个单独的消费者 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
Link 去围棋游乐场:https://play.golang.org/p/9RIvFsGKI-
你有一个死锁,因为你的偶数和奇数 goroutines 在发送到 out
时被阻止,因为没有从它读取任何东西。为什么没有读取 out
?因为 main
goroutine 在发送到 input
时被阻塞,因为没有任何东西从它读取。为什么 input
没有任何内容?因为从中读取的两个 goroutines 被阻塞了。
此外,filterEven
和 filterOdd
都只会 运行 一次,除非你将它们的内容包装在 for { }
中(但是它们永远不会停止,直到你 break
).另一方面,range even
将在没有任何内容可写入 even
时阻塞(并且 range odd
永远不会发生),因为通道上的 range
仅在通道为关闭或调用 break
。
总的来说,只要知道什么时候可以关闭频道,这些都不是很难解决的问题。根据您的描述,这变得更加困难。 None 的 goroutines 知道什么时候可以关闭 input
,因为所有三个都写入它,两个也从中读取。您可以使用 sync.WaitGroup
来确保您放入 input
通道的所有内容都已在关闭之前得到处理。一旦关闭,其他两个 goroutine 可以使用它作为关闭自己通道的信号,并 break
或 return
完成 运行ning.
但是,写入 in
和 out
通道仍然会阻塞,直到有相应的读取,因为它们是无缓冲的。但是,如果您通过将大小指定为 make
的第二个参数来缓冲它们,写入将不会阻塞,直到通道已满。由于您知道 even
或 odd
都不会比 main
发送给 input
的内容更多,因此您可以将其用作安全缓冲区容量。
下面是使用带缓冲通道的 WaitGroup
代码的示例:https://play.golang.org/p/VXqfwUwRcx
如果您不需要缓冲通道,您还可以使用另一对 goroutine 来捕获值并在完成后将它们作为切片发送回 main
。这种方式写入 even
和 odd
通道不会阻塞:https://play.golang.org/p/i5vLDcsK1v
否则,如果不需要一次打印每个通道的内容,您可以使用这两个额外的 goroutines 从通道读取并立即打印:https://play.golang.org/p/OCaUTcJkKB