[英]How to collect values from a channel into a slice in Go?
假設我有一個輔助函數helper(n int)
,它返回一段可變長度的整數。 我想運行helper(n)
並行的各種價值觀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
關閉。
ch
總是在負責推入ch
的例程結束時關閉。 因為程序在例程中推送到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
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.