Golang中如何并行运行这段代码
Golang中如何并行运行这段代码 大家好, 我有以下代码:
num := 100;
for i1 := 0; i1 < num; i1++ {
for i2 := 0; i2 < num; i2++ {
for i3 := 0; i3 < num; i3++ {
for i4 := 0; i4 < num; i4++ {
// 主体代码
}
}
}
}
如何以并行模式运行这段代码? 有什么想法可以提供帮助吗? 谢谢,
3 回复
正文代码是否引用了循环变量 i1、… i4?那么你可以启动一个 Goroutine:
// 正文代码
go func(a int, b int, c int, d int) {
// 执行某些操作
} (i1, i2, i3, i4) // 我们必须在这里捕获这些值!
但是,根据 num 的大小以及正文代码所执行的操作,这可能不是最佳方案。你将启动 num^4 个 goroutine,如果每个 goroutine 都使用某些有限的资源,你可能会使该资源过载。
在Go中并行运行嵌套循环的常见方法是使用goroutine和sync.WaitGroup。以下是几种实现方式:
方案1:并行最外层循环
package main
import (
"sync"
)
func main() {
num := 100
var wg sync.WaitGroup
for i1 := 0; i1 < num; i1++ {
wg.Add(1)
go func(i1 int) {
defer wg.Done()
for i2 := 0; i2 < num; i2++ {
for i3 := 0; i3 < num; i3++ {
for i4 := 0; i4 < num; i4++ {
// 主体代码
_ = i1 + i2 + i3 + i4
}
}
}
}(i1)
}
wg.Wait()
}
方案2:并行两个外层循环(使用工作池)
package main
import (
"sync"
)
func main() {
num := 100
var wg sync.WaitGroup
workerCount := 10 // 控制并发goroutine数量
jobs := make(chan [2]int, num*num)
// 启动worker
for w := 0; w < workerCount; w++ {
wg.Add(1)
go func() {
defer wg.Done()
for job := range jobs {
i1, i2 := job[0], job[1]
for i3 := 0; i3 < num; i3++ {
for i4 := 0; i4 < num; i4++ {
// 主体代码
_ = i1 + i2 + i3 + i4
}
}
}
}()
}
// 分发任务
for i1 := 0; i1 < num; i1++ {
for i2 := 0; i2 < num; i2++ {
jobs <- [2]int{i1, i2}
}
}
close(jobs)
wg.Wait()
}
方案3:使用errgroup管理goroutine(需要错误处理时)
package main
import (
"golang.org/x/sync/errgroup"
)
func main() {
num := 100
var g errgroup.Group
for i1 := 0; i1 < num; i1++ {
i1 := i1 // 创建局部变量副本
g.Go(func() error {
for i2 := 0; i2 < num; i2++ {
for i3 := 0; i3 < num; i3++ {
for i4 := 0; i4 < num; i4++ {
// 主体代码
_ = i1 + i2 + i3 + i4
}
}
}
return nil
})
}
_ = g.Wait()
}
方案4:并行所有循环(激进并行化)
package main
import (
"sync"
"runtime"
)
func main() {
num := 100
var wg sync.WaitGroup
sem := make(chan struct{}, runtime.NumCPU()*2) // 控制最大并发数
for i1 := 0; i1 < num; i1++ {
for i2 := 0; i2 < num; i2++ {
for i3 := 0; i3 < num; i3++ {
for i4 := 0; i4 < num; i4++ {
wg.Add(1)
sem <- struct{}{}
go func(i1, i2, i3, i4 int) {
defer wg.Done()
defer func() { <-sem }()
// 主体代码
_ = i1 + i2 + i3 + i4
}(i1, i2, i3, i4)
}
}
}
}
wg.Wait()
}
关键注意事项:
- 闭包变量捕获:在goroutine中使用循环变量时,必须通过参数传递或创建局部副本
- 内存消耗:方案4会创建1亿个goroutine,通常不推荐
- 任务粒度:根据主体代码的执行时间选择合适的并行粒度
- 资源限制:使用缓冲通道或worker池控制并发数量
选择哪种方案取决于:
- 主体代码的执行时间(CPU密集型还是I/O密集型)
- 内存限制
- 是否需要错误处理
- 期望的并行度控制
对于大多数情况,方案1或方案2是最实用的选择。

