
本文详解 go 语言中因切片(slice)的引用特性导致的并发竞态问题:当通过通道传递排列结果时,多个 goroutine 共享同一底层数组,引发重复输出、丢失排列等未定义行为,并提供线程安全的修复方案。
本文详解 go 语言中因切片(slice)的引用特性导致的并发竞态问题:当通过通道传递排列结果时,多个 goroutine 共享同一底层数组,引发重复输出、丢失排列等未定义行为,并提供线程安全的修复方案。
在 Go 中实现 Heap 算法并通过通道(chan []int64)向外流式输出所有排列时,看似简洁的代码却常出现结果重复、数量不足、内容错乱等问题——这并非算法逻辑错误,而是由 Go 切片的本质和并发模型共同导致的经典竞态(race condition)。
? 根本原因:切片是引用类型,非值类型
切片 []int64 本身是一个三元结构(指针 + 长度 + 容量),它不包含数据,仅指向底层数组。在你的 permch 函数中:
for slc := range ch
- ❌ 错误认知:“channel 关闭时机导致数据丢失” → 实际是共享内存竞态;
- ✅ 正确实践:向 channel 发送切片前,必须深拷贝(
append(..., a...)或copy(dst, a)); - ✅ 必做动作:用
go run -race检测并发安全性; - ✅ 清晰边界:
close(ch)仅表示“生产结束”,不影响已入队/正在传输的数据,range会自然退出。
修复后的程序将稳定输出全部 n! 个唯一排列,每个都是内存独立、内容确定的切片实例——这才是 Go 并发编程中“值安全”的正确打开方式。











