du5407 2016-06-16 10:18
浏览 97
已采纳

如何从以特定顺序执行的N个goroutine中收集值?

Below is a struct of type Stuff. It has three ints. A Number, its Double and its Power. Let's pretend that calculating the double and power of a given list of ints is an expensive computation.

type Stuff struct {
    Number int
    Double int
    Power  int
}

func main() {
    nums := []int{2, 3, 4} // given numbers
    stuff := []Stuff{}     // struct of stuff with transformed ints

    double := make(chan int)
    power := make(chan int)

    for _, i := range nums {
        go doubleNumber(i, double)
        go powerNumber(i, power)
    }

    // How do I get the values back in the right order?

    fmt.Println(stuff)
}

func doubleNumber(i int, c chan int) {
    c <- i + i
}

func powerNumber(i int, c chan int) {
    c <- i * i
}

The result of fmt.Println(stuff) should be the same as if stuff was initialized like:

stuff := []Stuff{
    {Number: 2, Double: 4, Power: 4}
    {Number: 3, Double: 6, Power: 9}
    {Number: 4, Double: 8, Power: 16}
}

I know I can use <- double and <- power to collect values from the channels, but I don't know what double / powers belong to what numbers.

  • 写回答

2条回答 默认 最新

  • douyinliu8813 2016-06-16 11:28
    关注

    Goroutines run concurrently, independently, so without explicit synchronization you can't predict execution and completion order. So as it is, you can't pair returned numbers with the input numbers.

    You can either return more data (e.g. the input number and the output, wrapped in a struct for example), or pass pointers to the worker functions (launched as new goroutines), e.g. *Stuff and have the goroutines fill the calculated data in the Stuff itself.

    Returning more data

    I will use a channel type chan Pair where Pair is:

    type Pair struct{ Number, Result int }
    

    So calculation will look like this:

    func doubleNumber(i int, c chan Pair) { c <- Pair{i, i + i} }
    
    func powerNumber(i int, c chan Pair) { c <- Pair{i, i * i} }
    

    And I will use a map[int]*Stuff because collectable data comes from multiple channels (double and power), and I want to find the appropriate Stuff easily and fast (pointer is required so I can also modify it "in the map").

    So the main function:

    nums := []int{2, 3, 4} // given numbers
    stuffs := map[int]*Stuff{}
    
    double := make(chan Pair)
    power := make(chan Pair)
    
    for _, i := range nums {
        go doubleNumber(i, double)
        go powerNumber(i, power)
    }
    
    // How do I get the values back in the right order?
    for i := 0; i < len(nums)*2; i++ {
        getStuff := func(number int) *Stuff {
            s := stuffs[number]
            if s == nil {
                s = &Stuff{Number: number}
                stuffs[number] = s
            }
            return s
        }
    
        select {
        case p := <-double:
            getStuff(p.Number).Double = p.Result
        case p := <-power:
            getStuff(p.Number).Power = p.Result
        }
    }
    
    for _, v := range nums {
        fmt.Printf("%+v
    ", stuffs[v])
    }
    

    Output (try it on the Go Playground):

    &{Number:2 Double:4 Power:4}
    &{Number:3 Double:6 Power:9}
    &{Number:4 Double:8 Power:16}
    

    Using pointers

    Since now we're passing *Stuff values, we can "pre-fill" the input number in the Stuff itself.

    But care must be taken, you can only read/write values with proper synchronization. Easiest is to wait for all "worker" goroutines to finish their jobs.

    var wg = &sync.WaitGroup{}
    
    func main() {
        nums := []int{2, 3, 4} // given numbers
    
        stuffs := make([]Stuff, len(nums))
        for i, n := range nums {
            stuffs[i].Number = n
            wg.Add(2)
            go doubleNumber(&stuffs[i])
            go powerNumber(&stuffs[i])
        }
        wg.Wait()
        fmt.Printf("%+v", stuffs)
    }
    
    func doubleNumber(s *Stuff) {
        defer wg.Done()
        s.Double = s.Number + s.Number
    }
    
    func powerNumber(s *Stuff) {
        defer wg.Done()
        s.Power = s.Number * s.Number
    }
    

    Output (try it on the Go Playground):

    [{Number:2 Double:4 Power:4} {Number:3 Double:6 Power:9} {Number:4 Double:8 Power:16}]
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥20 基于MSP430f5529的MPU6050驱动,求出欧拉角
  • ¥20 Java-Oj-桌布的计算
  • ¥15 powerbuilder中的datawindow数据整合到新的DataWindow
  • ¥20 有人知道这种图怎么画吗?
  • ¥15 pyqt6如何引用qrc文件加载里面的的资源
  • ¥15 安卓JNI项目使用lua上的问题
  • ¥20 RL+GNN解决人员排班问题时梯度消失
  • ¥60 要数控稳压电源测试数据
  • ¥15 能帮我写下这个编程吗
  • ¥15 ikuai客户端l2tp协议链接报终止15信号和无法将p.p.p6转换为我的l2tp线路