Go并发编程实战课笔记—RWMutex
目录
Go并发编程实战课笔记—RWMutex
以下为鸟窝大佬的Go 并发编程实战课 中摘录的笔记
针对读写场景,即考虑readers-writers
问题,同时可能有多个读或者多个写,但只要有一个线程在执行写操作,则其他线程都不能执行写操作,即读锁为共享锁,写锁为排他锁。
RWMutex标准库
- Lock/Unlock:写操作时调用的方法。
- RLock/RUlock:读操作时调用的方法。
- RLocker:返回调用RLock/RUnlock的Lokcer接口的对象。
当出现明确区分并发读写场景,且有大量的并发读和少量的并发写,可以考虑使用读写锁RWMutex替换Mutex。
RWMutex的实现原理
Go标准库中的RWMutex是基于Mutex实现的。
readers-writers问题一般有三类,基于对读和写操作的优先级,读写锁的设计和实现也分成三类:
- Read-preferring:读优先设计提供很高的并发型,但是会在竞争激烈的情况下导致写饥饿。
- Writer-preferring:写优先设计针对新来的请求优先保障writer,避免writer饥饿问题。
- 不指定优先级。
Go标准库中的RWMutex设计是写优先的方案,即一个正在阻塞的Lock调用会排除新的reader请求到锁。
|
|
RLock/Rulock实现
|
|
Lock/Unlock实现
|
|
陷阱
不可复制
互斥锁是不可复制的,再加上四个有状态的字段则更加不能复制使用,因为复制记录的状态与本身修改的状态不同步。
解决方案与互斥锁一样,可以借助vet工具进行检查。
重入导致死锁
重入导致的死锁情况较多且很难确认。
-
writer重入调用Lock时会出现死锁。
1 2 3 4 5
func re(l *sync.RWMutex){ l.Lock(); re(l); l.UnLock(); }
-
若在reader读操作时调用writer写操作,则会形成相互依赖的死锁关系。
-
环形依赖问题:writer依赖活跃的reader->活跃的reader依赖新来的reader->新来的reader依赖writer。
释放未加锁的RWMutex
使用读写锁的时候注意不要遗漏和多余。