第28章 同步原语
7 分钟阅读
第28章 同步原语
Go的sync包提供了各种同步原语,就像并发编程的"工具箱"。有了这些工具,多个协程才能安全地协作。
28.1 互斥锁 Mutex
28.1.1 为什么需要互斥锁
当多个协程同时访问共享变量时,会发生"数据竞争":
| |
28.1.2 保护共享资源
典型应用:保护银行账户:
| |
28.2 读写锁 RWMutex
互斥锁让所有访问都串行化,但"读多写少"的场景下,读锁可以并发,提高性能。
28.2.1 读写锁原理
- 读锁(RLock):允许多个读者同时读
- 写锁(Lock):独占,只允许一个写者
| |
28.2.2 什么时候用读写锁
| 场景 | 推荐锁 | 原因 |
|---|---|---|
| 读 » 写 | RWMutex | 读可以并发,提高性能 |
| 读写相当 | Mutex | 读写锁开销可能更大 |
| 写冲突 | Mutex | 保证写操作的原子性 |
28.3 等待组 WaitGroup
WaitGroup 用于等待一组协程完成:
| |
WaitGroup 三个方法:
Add(n)- 注册n个协程Done()- 协程完成时调用(等价于Add(-1))Wait()- 阻塞,直到计数归零
28.4 单次执行 Once
有些初始化操作只需要执行一次,Once 保证多次调用只执行一次:
| |
典型应用场景:
- 配置文件读取(只需要读一次)
- 数据库连接初始化
- 单例模式
28.5 并发安全 Map
sync.Map 是专门为并发访问设计的Map:
| |
sync.Map vs map+Mutex:
| 操作 | sync.Map | map+Mutex |
|---|---|---|
| 读 | 快 | 需要加锁 |
| 写 | 快 | 需要加锁 |
| 适合场景 | 高并发读 | 低并发 |
28.6 原子操作
对于简单的整数操作,原子操作比锁更高效:
28.6.1 原子整数
| |
28.6.2 CAS 操作
CAS(Compare-And-Swap)是原子操作的基础:
| |
原子操作类型:
AddInt64- 加法SwapInt64- 赋值LoadInt64- 读取StoreInt64- 存储CompareAndSwapInt64- CAS
28.7 条件变量 Cond
Cond 用于协程间的条件等待:
| |
Cond 的三个方法:
Wait()- 等待信号(自动释放锁)Signal()- 唤醒一个等待者Broadcast()- 唤醒所有等待者
本章小结
本章我们学习了同步原语:
互斥锁:
sync.Mutex:基本互斥锁sync.RWMutex:读写锁,读可以并发
等待组:
sync.WaitGroup:等待协程完成
单次执行:
sync.Once:多次调用只执行一次
并发安全Map:
sync.Map:专门为并发设计的Map
原子操作:
sync/atomic:高效的原子上操作
条件变量:
sync.Cond:协程间的条件等待
选择指南:
- 简单计数器 → 原子操作
- 读多写少 → RWMutex
- 复杂共享资源 → Mutex + map
- 等待多个协程 → WaitGroup
- 一次性初始化 → Once