在Go程序中合理使用goroutines

时间:2016-01-15 03:15:17

标签: go goroutine

我的程序有一个长期运行的任务。我有一个太大的列表jdIdList - 最多1000000项,所以下面的代码不起作用。有没有办法通过更好地使用goroutine来改进代码?

似乎我运行了太多的goroutines,这使我的代码无法运行。

有多少合理的goroutine可以运行?

var wg sync.WaitGroup
wg.Add(len(jdIdList))
c := make(chan string)

// just think jdIdList as [0...1000000]
for _, jdId := range jdIdList {
    go func(jdId string) {
        defer wg.Done()
        for _, itemId := range itemIdList {
            // following code is doing some computation which consumes much time(you can just replace them with time.Sleep(time.Second * 1)
            cvVec, ok := cvVecMap[itemId]
            if !ok {
                continue
            }
            jdVec, ok := jdVecMap[jdId]
            if !ok {
                continue
            }
            // long time compute
            _ = 0.3*computeDist(jdVec.JdPosVec, cvVec.CvPosVec) + 0.7*computeDist(jdVec.JdDescVec, cvVec.CvDescVec)
        }
        c <- fmt.Sprintf("done %s", jdId)
    }(jdId)

}

go func() {
    for resp := range c {
        fmt.Println(resp)
    }
}()

1 个答案:

答案 0 :(得分:2)

看起来你正在同时运行太多东西,使你的计算机内存不足。

这是您的代码的一个版本,它使用有限数量的工作器goroutines而不是像您的示例中的一百万个goroutines。由于只有少数goroutine同时运行,因此在系统开始交换之前,每个goroutine都有更多可用内存。确保内存每个小的计算需要的时间并发goroutine的数量少于系统中的内存,所以如果for jdId := range work循环内的代码需要少于1GB的内存,并且你有4个核心,至少4 GB的RAM,将clvl设置为4应该可以正常工作。

我还删除了等待组。代码仍然正确,但仅使用通道进行同步。通道上的范围循环从该通道读取,直到它关闭。这就是我们完成工作时告诉工作线程的方法。

https://play.golang.org/p/Sy3i77TJjA

runtime.GOMAXPROCS(runtime.NumCPU()) // not needed on go 1.5 or later
c := make(chan string)
work := make(chan int, 1) // increasing 1 to a higher number will probably increase performance
clvl := 4 // runtime.NumCPU() // simulating having 4 cores, use NumCPU otherwise
var wg sync.WaitGroup
wg.Add(clvl)
for i := 0; i < clvl; i++ {
    go func(i int) {
        for jdId := range work {
            time.Sleep(time.Millisecond * 100)
            c <- fmt.Sprintf("done %d", jdId)
        }
        wg.Done()
    }(i)
}

// give workers something to do
go func() { 
    for i := 0; i < 10; i++ {
        work <- i
    }

    close(work)
}()

// close output channel when all workers are done
go func() { 
    wg.Wait()
    close(c)
}()

count := 0
for resp := range c {
    fmt.Println(resp, count)
    count += 1
}

在go playground上生成此输出,同时模拟四个cpu核心。

done 1 0
done 0 1
done 3 2
done 2 3
done 5 4
done 4 5
done 7 6
done 6 7
done 9 8
done 8 9

请注意如何保证订购。 jdId变量保存您想要的值。您应该始终使用go race detector测试并发程序。

另请注意,如果您使用的是1.4或更早版本并且未将GOMAXPROCS环境变量设置为核心数,则应该这样做,或者将runtime.GOMAXPROCS(runtime.NumCPU())添加到程序的开头。