Go 中 sync.Mutex 是轻量互斥锁,需在临界区成对调用 Lock/Unlock;推荐 defer 解锁,结构体中嵌入 mutex 并传指针;避免值复制和死锁;读多写少时可用 RWMutex 提升性能。

在 Go 语言中,sync.Mutex 是最常用、最轻量的互斥锁机制,用于保护共享变量,防止多个 goroutine 同时读写导致数据竞争(data race)。正确使用 mutex 的关键是:**只在真正需要同步的临界区加锁,并确保成对调用 Lock() 和 Unlock()**。
理解 Mutex 的基本用法
sync.Mutex 提供两个核心方法:Lock()(阻塞获取锁)和 Unlock()(释放锁)。它不区分读写,属于“重量级”独占锁——一旦被某个 goroutine 持有,其他 goroutine 必须等待。
- 锁必须作用于**同一实例**:不同 mutex 实例之间互不影响;全局变量、结构体字段或局部指针传参需确保锁对象一致。
- 避免死锁:不要在持有锁时调用可能再次请求同一锁的函数;也不要在循环中无条件
Lock()而忘记Unlock()。 - 推荐用
defer mu.Unlock()确保释放,尤其在函数有多个返回路径时。
典型场景:保护结构体中的共享字段
最常见做法是把 sync.Mutex 作为结构体字段,配合封装方法控制访问:
type Counter struct {
mu sync.Mutex
value int
}
func (c *Counter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.value++
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.value
}
这样所有对 value 的修改和读取都经过锁保护,消除了并发读写冲突。注意:不能将 Counter 值类型变量直接传参(会复制 mutex),应始终传递指针。
立即学习“go语言免费学习笔记(深入)”;
避免常见错误:锁粒度与复制问题
锁太粗会导致性能瓶颈,锁太细或漏锁则起不到保护作用。还要警惕隐式复制:
-
错误示例(值拷贝):
var c Counter; go c.Inc()——c被复制,每个 goroutine 操作的是独立副本的mu,完全没效果。 -
错误示例(延迟解锁失效):
mu.Lock(); if err != nil { return }; mu.Unlock()—— 错误提前返回时锁未释放,造成后续 goroutine 永久阻塞。 -
建议: 使用
go vet -race静态检测数据竞争;运行时开启-race标志(go run -race main.go)可动态发现多数竞态问题。
进阶技巧:读多写少用 RWMutex
若共享资源读操作远多于写操作,可用 sync.RWMutex 提升并发读性能:
-
RLock()和RUnlock()允许多个 goroutine 同时读; -
Lock()和Unlock()仍为独占写锁,会阻塞所有读写; - 注意:
RWMutex不是锁升级机制——不能在已持RLock()的 goroutine 中直接调用Lock(),否则死锁。
例如缓存结构体中,Get() 用 RLock(),Set() 用 Lock(),能显著提升高并发读场景吞吐量。










