为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

凤凰求蛊 2022-09-12 16:28:14
假设我有一个帮助器函数,它返回一段可变长度的整数。我想并行运行各种值,并将输出收集在一个大切片中。我的第一次尝试如下:helper(n int)helper(n)npackage mainimport (    "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]似乎问题在于正在 goroutine 中更新,但函数在完成更新之前返回。outmain可行的一件事是使用大小为 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 但无法找到与我的用例非常匹配的内容。有什么想法吗?
查看完整描述

1 回答

?
慕尼黑8549860

TA贡献1818条经验 获得超11个赞

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


ch始终在负责推送到 的例程结束时关闭。由于程序在例程中推送到,因此不需要使用缓冲通道。chch


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

}


查看完整回答
反对 回复 2022-09-12
  • 1 回答
  • 0 关注
  • 53 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信