Go同步原语基于goroutine+channel模型,Mutex锁临界区而非变量,RWMutex明确读写意图,WaitGroup计数协作任务,sync与channel应混用各司其职。

Go 的同步原语不是“多线程语言通用逻辑”的复刻,它建立在 goroutine + channel 通信模型之上,核心逻辑是:**用显式同步控制弥补通信的边界,而不是替代通信**。
sync.Mutex 不是用来“锁住数据”,而是“锁住临界区的执行权”
很多人初学时误以为 Mutex 是给变量加锁,其实它锁的是代码段。Go 没有“变量级锁”概念,Lock() 和 Unlock() 之间那一段才是关键——这段代码必须串行执行,否则共享变量(比如 count)就可能被多个 goroutine 同时读-改-写。
- 必须成对出现:
Lock()后必须有对应的Unlock(),且不能跨函数边界漏掉(defer是最稳妥的写法) - 不能在已持有锁的 goroutine 中再次调用
Lock()(会死锁),Go 的Mutex不支持重入 - 锁的粒度要小:只包裹真正需要互斥的操作,比如只锁
count++,而不是整个 HTTP handler 函数
sync.RWMutex 的读写分离不是性能优化噱头,而是读多写少场景下的可见性契约
RWMutex 的 RLock()/RUnlock() 允许多个 goroutine 并发读,但不保证读到的是“最新写入值”,除非写操作已完成并释放了写锁。它的价值不在“快”,而在“明确表达意图”:你声明“这一块只读”,Go 运行时和审查者都能据此判断并发安全边界。
- 写操作必须用
Lock()/Unlock(),会阻塞所有新进的读和写 - 读锁不阻塞其他读锁,但会阻塞后续写锁的获取(直到所有读锁释放)
- 不能嵌套使用:比如在持有
RLock()时再调Lock(),会死锁
sync.WaitGroup 不是“等 goroutine 结束”,而是“等一组协作任务完成”
WaitGroup 的本质是计数器 + 通知机制,它不关心 goroutine 是否 panic、是否卡死,只认 Done() 调用次数是否等于初始 Add() 值。它解决的是“主流程需等待子任务全部产出结果”这类协作问题,不是生命周期管理。
立即学习“go语言免费学习笔记(深入)”;
-
Add()必须在 goroutine 启动前调用,否则可能Wait()已返回而新 goroutine 才刚启动 -
Done()应该放在defer里,确保 panic 时也能触发 - 不要重复
Add(1)后又Add(-1)来“修正”,这容易引发负计数 panic
最容易被忽略的一点是:sync 原语和 channel 不是二选一的关系。比如用 channel 传递任务,用 Mutex 保护本地缓存更新,用 WaitGroup 等待所有任务提交完成——它们各守一段职责,混用才是常态。


















