这里是一个小的示例程序与基本架构/流,我试图得到工作。如何打印出所有的数字和"结束"消息?我已经尝试在这里和那里放置关闭语句,但它要么不起作用,要么我对试图关闭已经关闭的通道感到恐慌…
package main
import (
"fmt"
"time"
)
func main() {
d := make(chan uint)
go bar(d)
c1 := make(chan uint)
c2 := make(chan uint)
c3 := make(chan uint)
go foo(c1, d)
go foo(c2, d)
go foo(c3, d)
c1 <- 1
c2 <- 2
c3 <- 3
c1 <- 4
c2 <- 5
c3 <- 6
c1 <- 7
c2 <- 8
c3 <- 9
}
func foo(c chan uint, d chan uint) {
fmt.Println("foo start")
for stuff := range c {
time.Sleep(1)
d <- stuff * 2
}
fmt.Println("foo end")
}
func bar(d chan uint) {
fmt.Println("bar start")
for stuff := range d {
fmt.Printf("bar received %dn", stuff)
}
fmt.Println("bar end")
}
我得到的输出看起来像这样。注意,最后一组数字和"end"输出丢失了。
foo start
bar start
foo start
foo start
bar received 6
bar received 2
bar received 4
bar received 12
bar received 8
bar received 10
在我的实际程序中,每个"foo"函数都在做过滤和一堆重字符串的正则表达式。我还需要"bar"函数,因为它的工作是根据时间戳重新排序,并序列化打印,这样输出就不会交错。
您的程序在所有例程完成之前退出。在从main
返回之前,需要等待foo
和bar
的程序都完成。
通常的方法是使用sync.WaitGroup
,但由于main
不是d
通道的生产者,您必须确保该通道上的所有发送都在使用第二个WaitGroup(或等效的)关闭之前完成。
var (
fooWG sync.WaitGroup
barWG sync.WaitGroup
)
func main() {
d := make(chan uint)
barWG.Add(1)
go bar(d)
c1 := make(chan uint)
c2 := make(chan uint)
c3 := make(chan uint)
fooWG.Add(3)
go foo(c1, d)
go foo(c2, d)
go foo(c3, d)
c1 <- 1
c2 <- 2
c3 <- 3
c1 <- 4
c2 <- 5
c3 <- 6
c1 <- 7
c2 <- 8
c3 <- 9
// close the channels so the foo goroutines can exit
close(c1)
close(c2)
close(c3)
fooWG.Wait()
// all foo are done, so it's safe to close d and wait for bar
close(d)
barWG.Wait()
}
func foo(c chan uint, d chan uint) {
defer fooWG.Done()
fmt.Println("foo start")
for stuff := range c {
time.Sleep(1)
d <- stuff * 2
}
fmt.Println("foo end")
}
func bar(d chan uint) {
defer barWG.Done()
fmt.Println("bar start")
for stuff := range d {
fmt.Printf("bar received %dn", stuff)
}
fmt.Println("bar end")
}
JimB的答案确实有效,但它增加了代码中实际需要的更多复杂性。一个简单的完整通道就足以通过完成来同步此代码。
此外,使用通道同步,不再需要time.Sleep(1)
命令来实现功能:
package main
import (
"fmt"
"time"
)
func main() {
d := make(chan uint)
complete := make(chan bool)
go bar(d, complete)
c1 := make(chan uint)
c2 := make(chan uint)
c3 := make(chan uint)
go foo(c1, d)
go foo(c2, d)
go foo(c3, d)
c1 <- 1
c2 <- 2
c3 <- 3
c1 <- 4
c2 <- 5
c3 <- 6
c1 <- 7
c2 <- 8
c3 <- 9
//If you know the number of inputs, count them to ensure completion
for i:=0; i < 9; i++{
<-complete
}
//Clean up after yourself, to keep away the memory leaks
close(c1)
close(c2)
close(c3)
close(d)
//Verify bar is done and closed correctly
<-complete
close(complete)
}
func foo(c chan uint, d chan uint) {
fmt.Println("foo start")
for stuff := range c {
time.Sleep(1) //Not needed for the program to function
d <- stuff * 2
}
fmt.Println("foo end")
}
func bar(d chan uint, cmp chan bool) {
fmt.Println("bar start")
for stuff := range d {
fmt.Printf("bar received %dn", stuff)
cmp <- true
}
fmt.Println("bar end")
//verify that cmp can be closed (all output is done, and d is closed)
cmp <- true
}