
本文讲解如何正确实现切片元素的并发处理:使用 `sync.waitgroup` 安全启动 goroutine 并写入结果切片,避免闭包变量捕获错误和通道索引越界问题。
在 Go 中实现“对切片每个元素并发调用函数并汇总结果”,关键在于避免共享变量竞争、正确传递循环变量、确保主协程等待所有子协程完成。原代码中尝试用 []chan int 存储通道,但未初始化切片容量就直接索引赋值(chans[i] = ...),导致 panic;同时闭包内直接引用循环变量 counter 和 number,会因 goroutine 延迟执行而捕获到错误的终值(经典的“循环变量陷阱”)。
更简洁、高效且符合 Go 习惯的做法是:预先分配结果切片 + 使用 sync.WaitGroup 同步 + 显式传参避免闭包捕获问题。无需额外通道层,既降低复杂度,又提升性能。
以下是推荐的 parallel 函数实现:
func parallel(arr []int) []int {
outArr := make([]int, len(arr)) // 预分配,支持按索引安全写入
var wg sync.WaitGroup
for i, num := range arr {
wg.Add(1)
go func(index int, value int) {
defer wg.Done()
outArr[index] = double(value) // 直接写入对应位置
}(i, num) // 立即传值,避免闭包引用循环变量
}
wg.Wait() // 主协程阻塞等待所有 goroutine 完成
return outArr
}⚠️ 注意事项:
- 必须预分配 outArr:make([]int, len(arr)) 确保底层数组已存在,支持并发写入(索引互不重叠,无竞态);
- 必须显式传参给 goroutine 函数:go func(i, n int) { ... }(i, num),防止所有 goroutine 共享同一个 i/num 变量;
- 必须使用 sync.WaitGroup:wg.Add() / wg.Done() / wg.Wait() 是保障结果完整性的核心机制;
- 避免使用未初始化的 []chan int:若坚持用通道方案,应先 chans = make([]chan int, len(arr)),再 chans[i] = make(chan int),且需额外 goroutine 收集结果——这增加了不必要的复杂度与开销。
最终,parallel 函数执行时间约等于单次 double 耗时(500ms),而非串行累加(1500ms),真正实现了并行加速。该模式广泛适用于 I/O 密集型或计算密集型批量任务,是 Go 并发编程中的标准实践之一。









