玩转 go,我把这段代码放在一起:
package main
import "fmt"
const N = 10
func main() {
ch := make(chan int, N)
done := make(chan bool)
for i := 0; i < N; i++ {
go (func(n int, ch chan int, done chan bool) {
for i := 0; i < N; i++ {
ch <- n*N + i
}
done <- true
})(i, ch, done)
}
numDone := 0
for numDone < N {
select {
case i := <-ch:
fmt.Println(i)
case <-done:
numDone++
}
}
for {
select {
case i := <-ch:
fmt.Println(i)
default:
return
}
}
}
基本上我有 N 个 channel 在做一些工作并在同一个 channel 上报告——我想知道所有 channel 什么时候完成。所以我有另一个
done
channel ,每个工作 goroutine 都会发送一条消息(消息无关紧要),这导致 main 将该线程计数为完成。当计数达到 N 时,我们实际上就完成了。这是“好”吗?有没有更惯用的方法来做到这一点?
编辑:为了澄清一点,我很怀疑,因为
done
channel 似乎正在做一个 channel 关闭似乎是为了的工作,但当然我实际上不能在任何 goroutine 中关闭 channel ,因为所有例程共享相同 channel 。所以我使用 done
来模拟一个执行某种“缓冲关闭”的 channel 。编辑2:原始代码并没有真正起作用,因为有时来自例程的
done
信号在 int 之前被读取,它只是放在 ch
上。需要一个“清理”循环。 最佳答案
这里有一个sync.WaitGroup的惯用用法供大家学习
( playground link )
package main
import (
"fmt"
"sync"
)
const N = 10
func main() {
ch := make(chan int, N)
var wg sync.WaitGroup
for i := 0; i < N; i++ {
wg.Add(1)
go func(n int) {
defer wg.Done()
for i := 0; i < N; i++ {
ch <- n*N + i
}
}(i)
}
go func() {
wg.Wait()
close(ch)
}()
for i := range ch {
fmt.Println(i)
}
}
注意两个go例程定义中闭包的使用,注意第二个
go
语句等待所有例程完成,然后关闭 channel ,这样range
就可以使用了。关于go - 编写此代码的更好的惯用方式?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/16020406/