Go程序中goroutines的合理使用
Reasonable use of goroutines in Go programs
我的程序有一个很长的 运行ning 任务。我的列表 jdIdList
太大了 - 最多 1000000
项,所以下面的代码不起作用。有没有更好地使用 goroutines 来改进代码的方法?
看来我的 goroutines 太多了 运行ning 这使得我的代码无法 运行。
拥有 运行ning 的 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)
}
}()
您似乎 运行 同时处理了太多事情,导致您的计算机 运行 内存不足。
这是您的代码版本,它使用有限数量的 worker goroutine,而不是您示例中的一百万个 goroutine。由于一次只有几个 goroutines 运行,因此在系统开始交换之前,它们每个都有更多可用内存。确保每个小计算所需的内存乘以并发 goroutine 的数量小于您系统中的内存,因此如果 for jdId := range work
循环中的代码需要小于 1GB 内存,并且您有 4 个内核并且至少4 GB RAM,将 clvl
设置为 4
应该可以正常工作。
我还删除了等待组。代码仍然正确,但仅使用通道进行同步。通道上的 for range 循环从该通道读取,直到它关闭。这就是我们在完成时告诉工作线程的方式。
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.
测试您的并发程序
另请注意,如果您使用的是 go 1.4 或更早版本并且尚未将 GOMAXPROCS 环境变量设置为核心数,则您应该这样做,或者将 runtime.GOMAXPROCS(runtime.NumCPU())
添加到程序的开头。
我的程序有一个很长的 运行ning 任务。我的列表 jdIdList
太大了 - 最多 1000000
项,所以下面的代码不起作用。有没有更好地使用 goroutines 来改进代码的方法?
看来我的 goroutines 太多了 运行ning 这使得我的代码无法 运行。
拥有 运行ning 的 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)
}
}()
您似乎 运行 同时处理了太多事情,导致您的计算机 运行 内存不足。
这是您的代码版本,它使用有限数量的 worker goroutine,而不是您示例中的一百万个 goroutine。由于一次只有几个 goroutines 运行,因此在系统开始交换之前,它们每个都有更多可用内存。确保每个小计算所需的内存乘以并发 goroutine 的数量小于您系统中的内存,因此如果 for jdId := range work
循环中的代码需要小于 1GB 内存,并且您有 4 个内核并且至少4 GB RAM,将 clvl
设置为 4
应该可以正常工作。
我还删除了等待组。代码仍然正确,但仅使用通道进行同步。通道上的 for range 循环从该通道读取,直到它关闭。这就是我们在完成时告诉工作线程的方式。
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.
另请注意,如果您使用的是 go 1.4 或更早版本并且尚未将 GOMAXPROCS 环境变量设置为核心数,则您应该这样做,或者将 runtime.GOMAXPROCS(runtime.NumCPU())
添加到程序的开头。