簡體   English   中英

如何將通道中的值收集到 Go 中的切片中?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM