
向 Go channel 发送值类型(如 *list.List 或 []string)时,仅发生一次浅拷贝:通道内部复制的是值本身(如指针或结构体),而非其指向的底层数据;因此开销接近于复制指针或小结构体,但需注意并发修改共享数据的风险。
向 go channel 发送值类型(如 `*list.list` 或 `[]string`)时,仅发生一次浅拷贝:通道内部复制的是值本身(如指针或结构体),而非其指向的底层 data;因此开销接近于复制指针或小结构体,但需注意并发修改共享数据的风险。
在 Go 中,channel 通信是值传递语义——每次发送操作都会将被发送值的一个副本写入 channel 的缓冲区(如有),接收时再将该副本拷贝给接收方变量。关键在于:这个“值”是什么?
- 若发送的是
*list.List(推荐做法),实际拷贝的只是一个指针(通常 8 字节),极轻量; - 若发送的是
list.List(结构体本身,非指针),则拷贝其两个字段:root listElement和len int(见container/list/list.go#L48),仍是常量时间、低开销的浅拷贝; - 若发送的是
[]string,拷贝的是 slice 头部(3 字段:ptr, len, cap),不复制底层数组内容,同样为 O(1) 浅拷贝。
✅ 正确示例(高效且安全):
ch := make(chan *list.List, 10)
l := list.New()
l.PushBack("hello")
ch <p>⚠️ 风险提示(核心 gotcha):<br>
虽然 channel 本身只做浅拷贝,但若多个 goroutine 通过该指针(如 <code>*list.List</code>)<strong>并发读写同一链表元素</strong>,将导致数据竞争(data race)。Go 不会自动同步访问——你必须自行保证: </p>
- 要么仅由一个 goroutine 修改该列表(生产者/消费者模型下常见);
- 要么使用
sync.Mutex/sync.RWMutex保护临界区; - 或改用线程安全的数据结构(如
sync.Map,但注意containers/list无内置并发安全版本)。
? 总结:
Channel 传输的开销本质是「值拷贝」,对指针、slice、struct 等均属浅拷贝,性能友好;真正的成本与风险来自共享可变状态的并发访问控制,而非 channel 本身。优化方向应聚焦于:避免不必要的深拷贝(如 json.Marshal 后传字符串)、合理设计所有权边界(谁创建、谁销毁、谁修改),而非担忧 channel 的复制开销。











