本文介绍如何使用 goroutine 和 sync.waitgroup 替代错误的通道切片方案,安全、简洁地实现整数切片的并发计算(如 `double` 函数),避免闭包变量捕获和索引越界问题,并确保结果顺序与输入一致。
在 Go 中实现切片元素的并发处理时,核心目标是:每个元素独立启动 goroutine 执行耗时操作(如 double(i)),所有 goroutine 并行运行,主协程等待全部完成后再统一返回有序结果。原代码中尝试用 []chan int 存储通道并手动索引赋值,但存在两个关键错误:
✅ 正确解法是:预分配结果切片 + sync.WaitGroup 同步 + 显式传参避免闭包捕获。以下是优化后的完整实现:
package main
import (
"fmt"
"sync"
"time"
)
func double(i int) int {
result := 2 * i
fmt.Printf("double(%d) = %d\n", i, result)
time.Sleep(500 * time.Millisecond) // 更清晰的写法
return result
}
func notParallel(arr []int) []int {
outArr := make([]int, 0, len(arr))
for _, i := range arr {
outArr = append(outArr, double(i))
}
return outArr
}
func parallel(arr []int) []int {
n := len(arr)
outArr := make([]int, n) // 预分配,保证顺序和容量
var wg sync.WaitGroup
for i, num := range arr {
wg.Add(1)
// 显式将 i 和 num 作为参数传入 goroutine,避免闭包捕获
go func(index int, value int) {
defer wg.Done()
outArr[index] = double(value)
}(i, num)
}
wg.Wait() // 阻塞直到所有 goroutine 完成
return outArr
}
func main() {
arr := []int{7, 8, 9}
fmt.Println("串行执行:")
fmt.Printf("结果: %v\n", notParallel(arr))
fmt
.Println("\n并发执行:")
fmt.Printf("结果: %v\n", parallel(arr))
}? 关键要点说明:
? 扩展建议:若需处理不确定数量或流式数据,或需错误传播、超时控制,则应选用带缓冲的通道(如 chan Result)配合 select 和 context,但对固定切片的映射类操作,WaitGroup + 预分配切片 是最轻量、最直观的并发模式。