Mutex 的功能和用法
Mutex 的功能和用法
提示
题眼: 并发安全,重点理解 4 种状态、正常模式、饥饿模式和自旋。
Mutex 为 Go 提供了最基础的互斥锁能力。它内部主要有两个字段:state 和 sema。其中 state 是一个 int32,用于表示锁当前的状态;sema 是信号量,用于阻塞和唤醒 goroutine。
state 中可以反映出以下几类信息:
Locked:当前锁是否已被持有。Woken:是否已经有 goroutine 被唤醒并参与抢锁。Starving:当前是否处于饥饿模式。Waiter:等待锁的 goroutine 数量。
新版本里还增加了
TryLock。
Mutex 对外最常用的方法就是 Lock 和 Unlock。当加锁失败时,goroutine 不一定立刻阻塞,而是可能先进入自旋阶段,尝试在短时间内抢到锁。自旋可以减少协程切换开销,提高短临界区场景下的性能。
Mutex 存在两种模式:
- 正常模式:优先让新来的 goroutine 参与竞争,必要时自旋抢锁。
- 饥饿模式:等待时间过长的 goroutine 会被优先唤醒并直接拿锁,避免长期抢不到锁。
重要
参考答案:
互斥锁主要用于保护临界区,保证同一时刻只有一个 goroutine 能访问共享数据。它的底层依赖原子操作、运行时调度和信号量机制来完成加锁、阻塞和唤醒。
从实现上看,Mutex 既不是单纯的一把“开关锁”,也不是一旦抢不到就立刻睡眠。它会先尝试快速加锁;如果失败,再根据当前状态决定是否自旋;自旋还不成功,才会把 goroutine 挂到等待队列里。这样做的目的是在低冲突场景下减少调度成本。
Go 的 Mutex 还有正常模式和饥饿模式两套行为。正常模式更强调吞吐量,允许新来的 goroutine 一起竞争;饥饿模式更强调公平性,防止某些 goroutine 长时间抢不到锁。
使用时要注意几点。第一,锁和解锁一定要成对出现,常见写法是加锁后立刻 defer mu.Unlock()。第二,互斥锁不是可重入锁,同一个 goroutine 重复加锁会导致死锁。第三,对未加锁的互斥锁执行 Unlock 会直接 panic。另外,互斥锁和 WaitGroup 一样,都不建议随意复制后再使用。
