This repository has been archived on 2026-05-19. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
obsidian/DEV/GO/Channel vs Mutex.md
T

86 lines
2.8 KiB
Markdown
Raw Normal View History

2026-04-24 23:45:19 +08:00
---
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详解]]