--- tags: [go, golang, channel, mutex, 并发, 同步] create time: 2026-04-24 10:30 --- # Channel vs Mutex:为什么 Go 发明了 Channel? ## 概述 Mutex 已经能做同步了,Go 为什么还要发明 Channel?本文从抽象层次、安全性、组合性等角度分析 Channel 相比 Mutex 的核心优势,以及两者的适用场景。 ## 核心差异:锁"状态" vs 传递"数据" Mutex 保护的是**共享状态(数据)**,语义是"互斥访问":拿到锁 → 读写共享变量 → 释放锁。 Channel 传递的是**数据本身(消息)**,语义是"同步地传递":发送 → 接收。数据的所有权随 channel 转移,而不是共享。 > **核心区别:Mutex 让你"共享内存",Channel 让你"传递内存"。** ## Channel 相比 Mutex 的四大优势 ### 1. 数据所有权转移,比"共享访问"更安全 ```go // Mutex 模式:数据仍然在外部被共享 var data []int mu.Lock() data = append(data, x) mu.Unlock() // Channel 模式:数据随 channel 转移 ch <- x // 发送后,main goroutine 就不再持有这份数据 ``` Mutex 方案下,任何持有 mu 的 goroutine 都能读写 data —— 必须在每一处都正确加锁。Channel 方案下,**数据发出去就归接收方所有**,不存在"谁在什么时候访问"的问题。 ### 2. select + 多路复用:Mutex 做不到 ```go select { case v := <-ch1: // 处理 ch1 case v := <-ch2: // 处理 ch2 case <-time.After(timeout): // 超时 } ``` 用 Mutex 无法实现"从多个通道中等待任意一个就绪"的模式。 ### 3. 类型层面的方向约束 ```go func processor(ch <-chan int) { ... } // 只能收,不能发 func producer(ch chan<- int) { ... } // 只能发,不能收 ``` 在编译期就明确了数据流向。Mutex 做不到这一点 —— `*sync.Mutex` 不告诉你它保护的是哪个变量。 ### 4. 同步 + 通信,一举两得 Mutex 只做同步(互斥),不传递数据。Channel 同时完成**同步**(发送阻塞到接收就绪)和**通信**(传递数据值)。 ## 什么时候该用 Mutex? Channel 不是万能药,Mutex 也有它的价值: | 场景 | 推荐 | |------|------| | 保护局部共享状态(struct 多个字段被并发读写) | **Mutex** | | 高频访问的共享计数器、缓存 | **Mutex**(性能更好) | | goroutine 间的消息传递、管道组合 | **Channel** | | 多路复用、超时控制、取消传播 | **Channel** | ## 两者配合使用 Channel 内部实现本身就用了 mutex 来保护缓冲区操作,说明它们是**不同抽象层次的互补工具**,而非互斥关系。实际 Go 代码中经常配合使用: - 用 Mutex 保护 Channel 的元数据 - 用 Channel 做 goroutine 间通信,用 Mutex 保护内部共享状态 ## 关联笔记 - [[DEV/GO/Channel详解]]