
本文深入解析 Go 语言核心信条“Don’t communicate by sharing memory; share memory by communicating”,阐明其本质是通过通道(channel)建立所有权移交与顺序化访问,从而避免数据竞争,而非字面意义上禁止共享内存。
本文深入解析 go 语言核心信条“don’t communicate by sharing memory; share memory by communicating”,阐明其本质是通过通道(channel)建立**所有权移交与顺序化访问**,从而避免数据竞争,而非字面意义上禁止共享内存。
Go 的这句经典格言常被初学者误读为“绝对不能共享内存”。实际上,它并非技术禁令,而是一种设计哲学与工程优先级的声明:与其让多个 goroutine 主动竞争读写同一块内存(需加锁、原子操作等复杂同步),不如通过 channel 显式传递数据或其所有权,使内存访问天然具有时序性与排他性。
关键在于理解“share memory by communicating”的真实含义——它不是说“把内存塞进 channel 发送出去”,而是指:当一个 goroutine 通过 channel 将某个变量(或其指针)发送给另一个 goroutine 时,它实质上将该变量的访问权(ownership)移交了出去;接收方在收到后才开始使用,发送方则应停止访问。这种基于消息传递的协作模式,天然规避了竞态条件。
例如,考虑两个 goroutine 协作处理一个 map:
func main() {
data := make(map[string]int)
ch := make(chan map[string]int)
go func() {
// 发送方:构建并发送 map(移交所有权)
data["answer"] = 42
ch <- data // 此刻,data 的所有权已转交接收方
}()
go func() {
// 接收方:仅在收到后才读取,且发送方不再访问
received := <-ch
fmt.Println("Received:", received["answer"]) // 安全:无竞态
}()
time.Sleep(time.Millisecond) // 简单等待,实际应用应使用 sync.WaitGroup
}⚠️ 注意事项:
- 未发送指针 ≠ 安全:若仅通过 channel 传递空信号(如 chan struct{})来同步,而仍让双方访问同一全局变量,则仍存在竞态——你的“Golang Lemma”推论不成立。Go 内存模型保证的是 channel 操作本身的 happens-before 关系,而非对任意内存地址的自动可见性。只有当数据本身(或其引用)通过 channel 传递,且双方遵循“发送即放弃、接收即独占”的约定时,才能真正实现安全共享。
- unbuffered channel 是关键:如你所限,非缓冲通道强制发送与接收同步发生,确保移交动作原子完成;缓冲通道可能引入延迟,需更谨慎设计所有权边界。
- 复杂结构同样适用:无论是 []byte、map[string]*User 还是自定义结构体,只要通过 channel 传递(值拷贝或指针),就可依托 Go 的内存模型和开发者约定,实现清晰的生命周期管理。
归根结底,这句话倡导的是一种通信驱动的并发范式:用 channel 作为协调契约,让内存的“共享”成为受控的、一次性的、有明确边界的事件,而非持续的、隐式的、易出错的共用。这正是 CSP(Communicating Sequential Processes)思想在 Go 中的优雅落地——并发安全,始于清晰的通信契约。


















