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

2.8 KiB
Raw Blame History

tags, create time
tags create time
go
golang
channel
mutex
并发
同步
2026-04-24 10:30

Channel vs Mutex:为什么 Go 发明了 Channel?

概述

Mutex 已经能做同步了,Go 为什么还要发明 Channel?本文从抽象层次、安全性、组合性等角度分析 Channel 相比 Mutex 的核心优势,以及两者的适用场景。

核心差异:锁"状态" vs 传递"数据"

Mutex 保护的是共享状态(数据),语义是"互斥访问":拿到锁 → 读写共享变量 → 释放锁。

Channel 传递的是数据本身(消息),语义是"同步地传递":发送 → 接收。数据的所有权随 channel 转移,而不是共享。

核心区别:Mutex 让你"共享内存",Channel 让你"传递内存"。

Channel 相比 Mutex 的四大优势

1. 数据所有权转移,比"共享访问"更安全

// 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 做不到

select {
case v := <-ch1:
    // 处理 ch1
case v := <-ch2:
    // 处理 ch2
case <-time.After(timeout):
    // 超时
}

用 Mutex 无法实现"从多个通道中等待任意一个就绪"的模式。

3. 类型层面的方向约束

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 保护内部共享状态

关联笔记