我构建了以下go代码。
这个想法是建立一个完成的通道和一个生成int通道的生成器。 将它们链接到2级管道中 chanNumbers:= pipeb(done,pipea(done,gen(done)))
几秒钟后,取消完成的频道。 我希望看到生成器和管道的两个阶段取消并返回,但是“ PipeX正在终止”文本只是随机出现,我真的不明白为什么。 会有想法吗?
package main
import (
"fmt"
"time"
)
func gen(done <-chan interface{}) <-chan int {
ret := make(chan int)
cx := 0
go func() {
for {
select {
case <-done:
fmt.Println("**Generator Terminates now")
time.Sleep(2 * time.Second)
fmt.Println("**Generator has terminated now")
close(ret)
return
case ret <- cx:
fmt.Printf("Gen : we push %d \n", cx)
cx = cx + 1
}
}
}()
fmt.Println("Generator has created and returned its channel")
return ret
}
func pipea(done <-chan interface{}, in <-chan int) <-chan int {
ret := make(chan int)
go func() {
for {
select {
case <-done:
fmt.Println("**pipeA terminates")
time.Sleep(2 * time.Second)
fmt.Println("**pipeA has terminated now")
close(ret)
return
case tmp, ok := (<-in):
if ok {
fmt.Printf("pipeA : we push %d \n", tmp)
ret <- tmp
} else {
in = nil
}
}
}
}()
return ret
}
func pipeb(done <-chan interface{}, in <-chan int) <-chan int {
ret := make(chan int)
go func() {
for {
select {
case <-done:
fmt.Println("**pipeB terminates")
time.Sleep(2 * time.Second)
fmt.Println("**pipeB has terminated now")
close(ret)
return
case tmp, ok := (<-in):
if ok {
fmt.Printf("pipeB : we push %d \n", tmp)
ret <- tmp
} else {
in = nil
}
}
}
}()
return ret
}
func main() {
done := make(chan interface{})
chanNumbers := pipeb(done, pipea(done, gen(done)))
go func() {
time.Sleep(2 * time.Second)
close(done)
}()
forloop:
for {
select {
case n := <-chanNumbers:
fmt.Printf("Received in main element : %d\n", n)
case <-done:
break forloop
}
}
//end of the main program
fmt.Println("Sleeping some seconds before termiating")
time.Sleep(8 * time.Second)
fmt.Println("exit...")
}
答案 0 :(得分:2)
您有四个正在运行的例程:
gen
,您的生成器,写入无缓冲的输出通道,直到done
pipeA
,从gen
读取,写入无缓冲的输出通道,直到done
pipeB
,从pipeA
读取,写入无缓冲的输出通道,直到done
main
,从pipeB
读到done
现在,当您关闭done
时,它完全取决于go例程看到该命令的顺序。
如果main
是第一个看到done
已关闭的人,它将打破for循环并停止从pipeB
消费。但是,如果pipeB
仍在尝试写入输出通道(ret <- tmp
),它将在此处阻塞;因此它永远不会到达<- done
部分。
有两种方法可以解决此问题:
done
,并让其他例程使用for n := range in { }
。 select
中,以便生成器和管道可以检测到done
关闭的时间。 或者,您可能想使用缓冲的输出通道,但是即使那样,仍然可能出现此问题。