
分段切片时别直接用 len(slice) 除以协程数
常见错误是把数组长度除以 goroutine 数量,得到每个段的大小,再用 slice[i*seg : (i+1)*seg] 切。但当数组长度不能被整除时,最后一段会越界或漏数据。
正确做法是用 min 和边界检查,或者更稳妥地预计算每段起止索引:
- 用
start := i * seg,end := min(start+seg, len(data)) - 确保
start 才启动 goroutine,避免空段 panic - Go 没有内置
min,得自己写func min(a, b int) int { if a
用 sync.WaitGroup 等待所有 goroutine 完成,别用 time.Sleep
新手常在启动一堆 goroutine 后加 time.Sleep(100 * time.Millisecond) 等结果,这不可靠:慢机器可能没跑完,快机器又白白等待。
必须显式同步:
立即学习“go语言免费学习笔记(深入)”;
- 在 goroutine 外声明
wg := &sync.WaitGroup{} - 每次启动前调用
wg.Add(1),goroutine 结束时 deferwg.Done() - 主流程最后阻塞等待
wg.Wait() - 注意
wg.Add()必须在 goroutine 启动前执行,否则可能 race
共享结果变量要加锁,别让多个 goroutine 直接写 result[i] = xxx
如果每个 goroutine 负责算一段并写回原数组对应位置(比如排序、转换),看似没冲突——但前提是写入的是不同索引。一旦逻辑出错(比如索引算偏),就会 data race。
更安全的做法:
- 让每个 goroutine 返回局部结果(如
[]int或结构体),主 goroutine 收集后合并 - 若必须原地写,且索引严格隔离,可用
sync/atomic写简单类型(如int64),但数组元素不适用 - 真要并发写同一 slice 的不同位置,需确认编译器不会优化掉边界检查,且运行时开启
go run -race测试
小数组别硬上 goroutine,调度开销可能比计算还贵
启动 goroutine 本身有成本:栈分配、调度入队、上下文切换。实测在 len(data) 且单个元素处理耗时 <p>建议按场景权衡:</p> <ul> <li>CPU 密集型(如加密、数值计算):单段至少 10k 元素再考虑分段</li> <li>IO 或阻塞操作多(如 http 请求):可更激进,几十个任务就上 goroutine</li> <li>不确定时,用 <code>benchstat 对比 go test -bench=. 结果,看 ns/op 是否真下降
真正难的不是怎么启 goroutine,是怎么判断“这一段该不该交给另一个 P”——runtime 调度器不透明,别假设你能精确控制谁在哪个线程跑。