这是一个小例子程序,其中包含我正在努力工作的基本架构/流程。我如何获得所有数字和#34;结束"打印出来的消息?我试过在这里和那里发表密切的陈述,但它要么不起作用,要么我对试图关闭已经关闭的频道感到恐慌......
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 %d\n", stuff)
}
fmt.Println("bar end")
}
我得到的输出看起来像这样。注意最后一组数字和&#34;结束&#34;输出缺失。
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
在我的实际节目中,每个&#34; foo&#34;函数正在做过滤和一堆重字符串正则表达式的东西。我需要&#34; bar&#34;函数,因为它具有基于时间戳的重新排序和序列化打印的工作,所以输出不会被隔行扫描。
答案 0 :(得分:4)
您的程序在所有goroutine完成之前退出。在从foo
返回之前,您需要等待bar
和main
goroutines完成。
通常的方法是使用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 %d\n", stuff)
}
fmt.Println("bar end")
}
答案 1 :(得分:1)
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 %d\n", stuff)
cmp <- true
}
fmt.Println("bar end")
//verify that cmp can be closed (all output is done, and d is closed)
cmp <- true
}