如何在并行for循环内使用通道

时间:2019-02-12 12:47:17

标签: go

func parallelSum (c chan int){
  sum := 0
  for i :=1 ; i< 100;i++{
    go func(i int){
        sum += i
    }(i)
  }
    time.Sleep(1*time.Second)
    c <- sum
}

我正在尝试学习并行功能,以加快诸如OpenMP之类的速度。这是Go中预期的总结并行循环的示例,此函数作为goroutine运行。

请注意,变量sum在这里不是通道,这是否意味着for循环内的变量sum访问是阻塞操作?现在足够有效了吗?有更好的解决方案吗?

我知道通道功能是为此目的而设计的,下面的我显然错误的工具可以编译,但是有100个类似以下的运行时错误。

goroutine 4 [chan receive]:
main.parallelSumError(0xc0000180c0)
    /home/tom/src/goland_newproject/main.go:58 +0xb4 //line 58 : temp := <-sum
created by main.main
    /home/tom/src/goland_newproject/main.go:128 +0x2ca //line 128: go parallelSumError(pcr), the calling function

那么这是什么问题?似乎求和不是并行for循环的一个很好的例子,但实际上我希望知道如何在并行for循环内使用通道。

func parallelSum (c chan int){
    sum := make(chan int)
    for i :=1 ; i< 100;i++{
        go func(i int){
            temp := <- sum //error here why?
            temp += i
            sum <- temp
        }(i)
    }
    time.Sleep(1*time.Second)
    temp := <-sum
    c <- temp
}

两者具有相同的主要功能

func main(){
    pc := make(chan int)
    go parallelSum(pc) 
    result = <- pc
    fmt.Println("parallel result:", result)
}

2 个答案:

答案 0 :(得分:1)

我不喜欢通过渠道对数字求和的想法。我宁愿使用诸如sync.Mutexatomic.AddUint64之类的经典名称。但是,至少,我使您的代码正常工作。 我们无法将值从一个通道传递到另一个通道(我添加了temp变量)。另外,还有sync.WaitGroup和其他内容。 但是我仍然不喜欢代码的想法。

package main

import (
"fmt"
"sync"
)

func main() {
    pc := make(chan int)
    go parallelSum(pc)
    result := <- pc
    fmt.Println("parallel result:", result)
}


func parallelSum (c chan int){
    sum := make(chan int)


    wg := sync.WaitGroup{}
    wg.Add(100)

    for i :=1 ; i <= 100;i++{
        go func(i int){
            temp := <- sum
            temp += i
            wg.Done()

            sum <- temp
        }(i)
    }

    sum <- 0

    wg.Wait()
    temp := <- sum
    c <- temp
}

答案 1 :(得分:1)

使用go例程(即go foo())时,最好通过内存共享使用通信。正如您所提到的,在此问题上,渠道是处理交流的通行方式。

对于您的特定应用,类似于OpenMP的并行和,最好检测CPU的数量并生成所需的例程:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    numCPU := runtime.NumCPU()
    sumc := make(chan int, numCPU)
    valuec := make(chan int)
    endc := make(chan interface{}, numCPU)

    // generate go routine per cpu
    for i := 0; i < numCPU; i++ {
        go sumf(sumc, valuec, endc)
    }

    // generate values and pass it through the channels
    for i := 0; i < 100; i++ {
        valuec <- i
    }

    // tell go routines to end up when they are done
    for i := 0; i < numCPU; i++ {
        endc <- nil
    }

    // sum results
    sum := 0
    for i := 0; i < numCPU; i++ {
        procSum := <-sumc
        sum += procSum
    }

    fmt.Println(sum)
}

func sumf(sumc, valuec chan int, endc chan interface{}) {
    sum := 0
    for {
        select {
        case i := <-valuec:
            sum += i
        case <-endc:
            sumc <- sum
            return
        }
    }
}

希望这会有所帮助。