Go需手动实现并发排序:按CPU核数切分数据→各goroutine独立排序子数组→channel传递结果→主goroutine多路归并;关键在避免底层数组重叠、合理设阈值、归并保序。
Go 本身不提供内置的并发排序函数,sort.Sort 是单线程的。若想利用多核加速排序,需手动切分数据、并发排序子数组、再归并。关键不是“开一堆 goroutine 排同一个 slice”,而是合理划分 + 安全归并。
常见错误是直接对共享 []int 启动多个 sort.Ints —— 这不仅不加速,还可能因数据重叠导致结果错乱。
sort.Ints 或自定义 sort.Sort
归并阶段不能简单把所有子结果 append 到一起——必须保持全局有序。典型做法是启动一个归并 goroutine,从多个排序完成的 channel 中取最小值。
为避免阻塞和资源泄漏,建议用带缓冲的 channel 传递子结果(例如 chan []int),并在发送前确保子 slice 已完全排序完毕。
chan []int
for range 接收所有子结果,存入切片切片 [][]int
mergeSortedSlices 函数归并(非并发,但时间复杂度可控)sort.Slice 本身无并发问题,但它操作的是传入的 slice 底层数组。如果多个 goroutine 同时对**同一底层数组的不同 slice 视图**调用 sort.Slice,只要这些视图互不重叠,就是安全的。
容易踩的坑:
data[i:j] 和 data[k:l] 时,底层指针相同且区间重叠 → 数据被多次改写copy 隔离子 slice,导致后续读取看到中间态数据sort.Slice)以下代码实现:将 []int 均分为 4 段,并发排序,再归并。适用于中等规模数据(几万到百万级),超过千万建议改用外部排序或更精细的分治策略。
package mainimport ( "sort" "sync" )
func concurrentMergeSort(data []int, numWorkers int) []int { n := len(data) if n <= 1 { return data } if numWorkers < 1 { numWorkers = 1 }
ch := make(chan []int, numWorkers) var wg sync.WaitGroup // 切分并启动 goroutine segmentSize := n / numWorkers for i := 0; i < numWorkers; i++ { start := i * segmentSize end := start + segmentSize if i == numWorkers-1 { end = n // 最后一段收尾 } if start >= n { break } wg.Add(1) go func(s, e int) { defer wg.Done() segment := make([]int, e-s) copy(segment, data[s:e]) sort.Ints(segment) ch <- segment }(start, end) } go func() { wg.Wait() close(ch) }() // 收集并归并 var sortedSegments [][]int for seg := range ch { sortedSegments = append(sortedSegments, seg) } return mergeSortedSlices(sortedSegments)}
func mergeSortedSlices
(segments [][]int) []int { if len(segments) == 0 { return nil } if len(segments) == 1 { return segments[0] }
result := make([]int, 0, 1024) indices := make([]int, len(segments)) for { minVal := 0 minIdx := -1 for i, seg := range segments { if indices[i] >= len(seg) { continue } if minIdx == -1 || seg[indices[i]] < minVal { minVal = seg[indices[i]] minIdx = i } } if minIdx == -1 { break } result = append(result, minVal) indices[minIdx]++ } return result}
真正难的不是开 goroutine,而是切分边界计算、归并时的内存局部性、以及小数据量下并发反而拖慢(调度开销 > 计算收益)。实测时建议用
benchstat对比sort.Ints和你的并发版本在不同长度下的表现。