Go 中通过 channel 传递切片时的数据竞争与深拷贝解决方案

张开发
2026/4/14 0:04:21 15 分钟阅读

分享文章

Go 中通过 channel 传递切片时的数据竞争与深拷贝解决方案
本文详解 go 并发编程中因复用同一底层数组切片导致 channel 接收端读取到重复或错误值的根本原因并提供安全、高效的深拷贝实践方案。 本文详解 go 并发编程中因复用同一底层数组切片导致 channel 接收端读取到重复或错误值的根本原因并提供安全、高效的深拷贝实践方案。在 Go 中切片[]byte并非值类型而是包含三个字段的结构体指向底层数组的指针、长度len和容量cap。这意味着当你将一个切片发送到 channel 时实际传递的是该结构体的副本——但所有副本共享同一底层数组。若发送方 goroutine 在后续迭代中持续修改该数组如 res[i]而接收方尚未消费前一次发送的切片就会发生典型的数据竞争data race接收端看到的可能是已被覆盖的中间状态从而出现值重复、跳变甚至 panic。你提供的代码正是这一问题的典型体现func combinations(result chan []byte, length int, min byte, max byte) { res : make([]byte, length) // ← 底层数组在此分配 for i : 0; i length; i { res[i] min } result - res // ← 发送切片指针指向 res 底层数组 for { i : 0 for i 0; i length; i { if res[i] max { res[i] // ← 直接修改底层数组 break } else { res[i] 32 } } result - res // ← 再次发送同一底层数组的切片 if i length { close(result) return } }}尽管 fmt.Printf 前的打印显示“发送正确”但那只是发送瞬间的状态接收端 range 循环读取时res 的内容早已被下一轮循环改写——因此输出大量重复值如 0100 出现两次本质是竞态条件下的未定义行为。? 正确解法每次发送前创建独立副本必须确保每个发送到 channel 的切片拥有专属底层数组。推荐使用 make copy 进行浅层深拷贝因元素为 byte无需递归func combinations(result chan []byte, length int, min byte, max byte) { res : make([]byte, length) for i : 0; i length; i { res[i] min } // 发送首组先拷贝再发送 buf : make([]byte, length) copy(buf, res) result - buf for { i : 0 for i 0; i length; i { if res[i] max { res[i] break } else { res[i] 32 } } // 关键修复每次发送前都新建副本 buf : make([]byte, length) copy(buf, res) result - buf if i length { close(result) return } }}? 提示也可封装为辅助函数提升可读性 幻导航网 发现优质实用网站,开启网络探索之旅

更多文章