面试题答案
一键面试- RWMutex简介
RWMutex
是Go语言标准库sync
包中用于读写锁的结构体。它允许多个读操作并发执行,但只允许一个写操作独占访问,以此保证数据一致性。
- 等待队列与调度基础
RWMutex
内部有一个等待队列来管理等待获取锁的协程。这个等待队列通过runtime_SemacquireMutex
和runtime_SemreleaseMutex
等底层原语来实现对协程的阻塞和唤醒。
- 写锁调度逻辑
- 获取写锁:
- 当一个协程尝试获取写锁时(调用
Lock
方法),它会先原子地检查当前读锁持有计数是否为0,并且没有其他协程在等待写锁。如果这两个条件都满足,它就获取到了写锁,此时写锁状态被设置为已锁定。 - 如果当前有读锁被持有(读锁持有计数不为0)或者有其他协程在等待写锁,那么该协程会被阻塞并放入等待队列。它会等待所有读锁释放并且等待队列中排在它前面的写锁请求都完成。
- 当一个协程尝试获取写锁时(调用
- 释放写锁:
- 当写操作完成并释放写锁(调用
Unlock
方法)时,它会先将写锁状态设置为未锁定。然后检查等待队列,如果等待队列中有等待获取写锁的协程,那么唤醒等待队列中第一个等待写锁的协程。如果等待队列中没有等待写锁的协程,但是有等待读锁的协程,那么唤醒所有等待读锁的协程。
- 当写操作完成并释放写锁(调用
- 获取写锁:
- 读锁调度逻辑
- 获取读锁:
- 当一个协程尝试获取读锁时(调用
RLock
方法),它会原子地增加读锁持有计数。如果此时没有写锁被持有(写锁状态为未锁定),则该协程成功获取读锁。 - 如果此时有写锁被持有或者有其他协程在等待写锁,那么该协程会被阻塞并放入等待队列,直到写锁被释放且等待队列中没有等待写锁的协程。
- 当一个协程尝试获取读锁时(调用
- 释放读锁:
- 当读操作完成并释放读锁(调用
RUnlock
方法)时,它会原子地减少读锁持有计数。如果读锁持有计数变为0,并且等待队列中有等待写锁的协程,那么唤醒等待队列中第一个等待写锁的协程。
- 当读操作完成并释放读锁(调用
- 获取读锁:
- 性能与一致性保证
- 性能方面:
- 读操作的并发执行提高了系统在读取频繁场景下的性能,因为多个读操作可以同时进行而不会互相阻塞。
- 写操作虽然会阻塞读操作,但由于写操作相对较少的场景假设,整体性能仍然能够得到保障。
RWMutex
通过高效的等待队列管理,减少了协程上下文切换的开销。
- 一致性方面:
- 写锁的独占特性保证了在写操作进行时,没有其他读或写操作可以同时进行,从而确保了数据一致性。读锁在获取时会检查是否有写锁或写锁等待,避免了在写操作进行中读取数据导致的数据不一致问题。
- 性能方面: