简体   繁体   English

如何使用通道收集来自各种 goroutine 的响应

[英]How to use channels to gather response from various goroutines

I am new to Golang and I have a task that I have implemented using WaitGroup , and Mutex which I would like to convert to use Channels instead.我是 Golang 的新手,我有一个使用WaitGroupMutex实现的任务,我想将其转换为使用Channels

A very brief description of the task is this: spurn as many go routines as needed to processes a result and in the main go routine wait and collect all the results.该任务的一个非常简短的描述是:根据需要拒绝尽可能多的 go 例程来处理结果,并在主 go 例程中等待并收集所有结果。

The implementation I have using WaitGroup , and Mutex is as follows:我使用WaitGroupMutex的实现如下:

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

func process(input int, wg *sync.WaitGroup, result *[]int, lock *sync.Mutex) *[]int {
    defer wg.Done()
    defer lock.Unlock()

    rand.Seed(time.Now().UnixNano())
    n := rand.Intn(5)
    time.Sleep(time.Duration(n) * time.Second)
    lock.Lock()
    *result = append(*result, input * 10)

    return result
}

func main() {

    var wg sync.WaitGroup
    var result []int
    var lock sync.Mutex
    for i := range []int{1,2,3,4,5} {
        wg.Add(1)
        go process(i, &wg, &result, &lock)
    }
}

How do I replace the memory synchronization with the usage of Mutex to one that uses Channels ?如何将使用Mutex的 memory 同步替换为使用Channels的同步?

My main problem is I am not sure how to determine the final go routine that is processing the final task and hence have that one be the one to close the channel .我的主要问题是我不确定如何确定正在处理最终任务的最终 go 例程,因此要关闭channel The idea is that by closing the channel the main go routine can loop over the channel , retrieve the results and when it sees the channel has been closed, it moves on.这个想法是,通过关闭channel ,主 go 例程可以循环遍历channel ,检索结果,当它看到channel已关闭时,它会继续前进。

It could also be that the approach to close the channel is the wrong one in this scenario, hence why I am asking here.在这种情况下,关闭通道的方法也可能是错误的,因此我在这里问。

How would a more experienced go programmer solve this problem using channels ?更有经验的 go 程序员如何使用channels解决这个问题?

Here's a solution using WaitGroup instead of waiting for a fixed number of results.这是使用WaitGroup而不是等待固定数量的结果的解决方案。

package main

import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)

func process(input int, wg *sync.WaitGroup, resultChan chan<- int) {
    defer wg.Done()

    rand.Seed(time.Now().UnixNano())
    n := rand.Intn(5)
    time.Sleep(time.Duration(n) * time.Second)

    resultChan <- input * 10
}

func main() {
    var wg sync.WaitGroup

    resultChan := make(chan int)

    for i := range []int{1,2,3,4,5} {
        wg.Add(1)
        go process(i, &wg, resultChan)
    }

    go func() {
        wg.Wait()
        close(resultChan)
    }()

    var result []int
    for r := range resultChan {
        result = append(result, r)
    }

    fmt.Println(result)
}

I change your code to use the channel.我更改了您的代码以使用该频道。 there are many other ways to use the channel.还有许多其他方法可以使用该频道。

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func process(input int, out chan<- int) {
    rand.Seed(time.Now().UnixNano())
    n := rand.Intn(5)
    time.Sleep(time.Duration(n) * time.Second)
    out <- input * 10
}

func main() {
    var result []int
    resultChan := make(chan int)
    items := []int{1, 2, 3, 4, 5}

    for _, v := range items {
        go process(v, resultChan)
    }

    for i := 0; i < len(items); i++ {
        res, _ := <-resultChan
        result = append(result, res)
    }

    close(resultChan)
    fmt.Println(result)
}

Update: (comment's answer)更新:(评论的答案)

if items count is unknown you need to signal the main to finish.如果项目数未知,则需要向主发出信号以完成。 otherwise "deadlock", you can create a channel to signal the main function to finish.否则“死锁”,您可以创建一个通道来通知主 function 完成。 also, you can use sync.waiteGroup .此外,您可以使用sync.waiteGroup

for panic in Goroutine, you can use defer and recover to handle errors.对于 Goroutine 中的 panic,你可以使用 defer 和 recover 来处理错误。 and you can create an error channel ore you can use x/sync/errgroup .你可以创建一个错误通道,你可以使用x/sync/errgroup

There are so many solutions.有很多解决方案。 and it depends on your problem.这取决于你的问题。 so there is no specific way to use goroutine, channel, and...所以没有具体的方式来使用 goroutine、channel 和...

Here's a sample snippet where I am using a slice of channels instead of waitgroups to perform a fork-join:这是一个示例片段,其中我使用通道切片而不是等待组来执行分叉连接:

package main

import (
    "fmt"
    "os"
)

type cStruct struct {
    resultChan chan int
    errChan    chan error
}

func process(i int) (v int, err error) {
    v = i
    return
}

func spawn(i int) cStruct {
    r := make(chan int)
    e := make(chan error)
    go func(i int) {
        defer close(r)
        defer close(e)
        v, err := process(i)
        if err != nil {
            e <- err
            return
        }
        r <- v
        return
    }(i)
    return cStruct{
        r,
        e,
    }
}

func main() {
    //have a slice of channelStruct
    var cStructs []cStruct
    nums := []int{1, 2, 3, 4, 5}
    for _, v := range nums {
        cStruct := spawn(v)
        cStructs = append(cStructs, cStruct)
    }
    //All the routines have been spawned, now iterate over the slice:
    var results []int
    for _, c := range cStructs {
        rChan, errChan := c.resultChan, c.errChan
        select {
        case r := <-rChan:
            {
                results = append(results, r)
            }
        case err := <-errChan:
            {
                if err != nil {
                    os.Exit(1)
                    return
                }
            }
        }

    }
    //All the work should be done by now, iterating over the results
    for _, result := range results {
        fmt.Println("Aggregated result:", result)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM