如何在Go中将通道中的值收集到切片中

  • 本文关键字:切片 通道 Go go
  • 更新时间 :
  • 英文 :


假设我有一个辅助函数helper(n int),它返回一个可变长度的整数切片。我想对n的各种值并行运行helper(n),并在一个大切片中收集输出。我的第一次尝试是:

package main
import (
"fmt"
"golang.org/x/sync/errgroup"
)
func main() {
out := make([]int, 0)
ch := make(chan int)
go func() {
for i := range ch {
out = append(out, i)
}
}()
g := new(errgroup.Group)
for n := 2; n <= 3; n++ {
n := n
g.Go(func() error {
for _, i := range helper(n) {
ch <- i
}
return nil
})
}
if err := g.Wait(); err != nil {
panic(err)
}
close(ch)
// time.Sleep(time.Second)
fmt.Println(out) // should have the same elements as [0 1 0 1 2]
}
func helper(n int) []int {
out := make([]int, 0)
for i := 0; i < n; i++ {
out = append(out, i)
}
return out
}

然而,如果我运行这个例子,我不会得到所有5个期望值,而是得到

[0 1 0 1]

(如果我取消对time.Sleep的注释,我会得到所有五个值[0 1 2 0 1],但这不是一个可接受的解决方案(。

这样做的问题似乎是out在goroutine中被更新,但main函数在完成更新之前返回。

一件可行的事情是使用大小为5的缓冲通道:

func main() {
ch := make(chan int, 5)
g := new(errgroup.Group)
for n := 2; n <= 3; n++ {
n := n
g.Go(func() error {
for _, i := range helper(n) {
ch <- i
}
return nil
})
}
if err := g.Wait(); err != nil {
panic(err)
}
close(ch)
out := make([]int, 0)
for i := range ch {
out = append(out, i)
}
fmt.Println(out) // should have the same elements as [0 1 0 1 2]
}

然而,尽管在这个简化的例子中,我知道输出的大小应该是多少,但在我的实际应用程序中,这并不是先验的。本质上,我想要的是一个"无限"缓冲区,这样发送到通道就不会阻塞,或者是一种更惯用的方式来实现同样的目的;我读过https://blog.golang.org/pipelines但无法找到与我的用例非常匹配的用例。有什么想法吗?

在此版本的代码中,执行被阻止,直到ch关闭。

CCD_ 9总是在负责推入CCD_ 10的例程结束时闭合。因为程序在例程中推送到ch,所以不需要使用缓冲通道。

package main
import (
"fmt"
"golang.org/x/sync/errgroup"
)
func main() {
ch := make(chan int)
go func() {
g := new(errgroup.Group)
for n := 2; n <= 3; n++ {
n := n
g.Go(func() error {
for _, i := range helper(n) {
ch <- i
}
return nil
})
}
if err := g.Wait(); err != nil {
panic(err)
}
close(ch)
}()
out := make([]int, 0)
for i := range ch {
out = append(out, i)
}
fmt.Println(out) // should have the same elements as [0 1 0 1 2]
}
func helper(n int) []int {
out := make([]int, 0)
for i := 0; i < n; i++ {
out = append(out, i)
}
return out
}

这是第一个代码的固定版本,它很复杂,但演示了sync.WaitGroup的用法。

package main
import (
"fmt"
"sync"
"golang.org/x/sync/errgroup"
)
func main() {
out := make([]int, 0)
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
for i := range ch {
out = append(out, i)
}
}()
g := new(errgroup.Group)
for n := 2; n <= 3; n++ {
n := n
g.Go(func() error {
for _, i := range helper(n) {
ch <- i
}
return nil
})
}
if err := g.Wait(); err != nil {
panic(err)
}
close(ch)
wg.Wait()
// time.Sleep(time.Second)
fmt.Println(out) // should have the same elements as [0 1 0 1 2]
}
func helper(n int) []int {
out := make([]int, 0)
for i := 0; i < n; i++ {
out = append(out, i)
}
return out
}

最新更新