Go中使用sync.Mutex保护共享数据的核心是所有对同一变量的读写操作都必须加锁且及时释放;典型场景包括计数器累加、缓存map操作、状态标志位修改等;推荐defer mu.Unlock()确保释放,结构体嵌入Mutex提升封装性,读多写少时可选RWMutex。

在 Go 语言中,用 sync.Mutex 保护共享数据,核心就一点:**所有对同一变量的读写操作,都必须在加锁后进行,且锁要及时释放**。不是“加了锁就安全”,而是“漏掉一次访问没加锁,就可能出问题”。
什么时候必须用 Mutex?
当多个 goroutine 同时访问(哪怕只是读+读)一个可变的全局变量、结构体字段或函数外的变量时,就存在数据竞争风险。典型场景包括:
- 计数器累加(如网站访问量统计)
- 缓存 map 的增删改查(非 sync.Map 场景)
- 状态标志位的修改与检查(如服务是否已关闭)
- 配置信息在运行时被热更新
基本写法:Lock/Unlock 成对出现
最常见也最容易出错的是忘记 Unlock(),或在多条 return 路径下遗漏解锁。推荐用 defer mu.Unlock() 确保释放:
var mu sync.Mutex
var counter int
func increment() {
mu.Lock()
defer mu.Unlock() // 即使中间 panic 或 return,也会执行
counter++
}
注意:不要在 Lock 前或 Unlock 后访问受保护的数据,也不要在持有锁时做耗时操作(如 HTTP 请求、文件读写),否则会阻塞其他 goroutine。
立即学习“go语言免费学习笔记(深入)”;
结构体中嵌入 Mutex 更清晰
把锁和它保护的数据组织在一起,逻辑更内聚:
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
defer c.mu.Unlock()
c.v[key]++
}
func (c *SafeCounter) Value(key string) int {
c.mu.Lock()
defer c.mu.Unlock()
return c.v[key]
}
这样调用者无需关心锁的存在,封装性更好。记得字段 v 不要直接导出(首字母小写),避免绕过锁直接访问。
读多写少?考虑 RWMutex
如果读操作远多于写操作,且读之间不互斥,可用 sync.RWMutex 提升并发性能:
-
RLock()+RUnlock():允许多个 goroutine 同时读 -
Lock()+Unlock():写操作独占,会阻塞所有读写
但注意:RWMutex 不是银弹。写少读多才明显受益;若写操作频繁,反而可能因升级锁(从 RLock 切换到 Lock)引发更多等待。
基本上就这些。Mutex 本身不复杂,关键在使用习惯——每次碰共享变量前,先问一句:“这里加锁了吗?”










