跳到主要内容

Go 语言并发学习 读写锁的原理

Golang 读写锁的特点

在 Golang 中,读写锁 sync.RWMutex 提供了对共享资源的读写访问控制,它具有以下特点:

  1. 允许多个 goroutine 同时读取共享资源,但只允许一个 goroutine 写入共享资源。
  2. 当有一个 goroutine 写入共享资源时,所有其他 goroutine(包括读取和写入)都被阻塞,直到写入完成。
  3. 当有一个 goroutine 读取共享资源时,其他读取的 goroutine 仍然可以继续读取,但不能进行写入。

底层实现结构

互斥锁对应的是底层结构是 sync.RWMutex 结构体,,位于 src/sync/rwmutex.go

type RWMutex struct {
w Mutex // 复用互斥锁
writerSem uint32 // 信号量,用于写等待读
readerSem uint32 // 信号量,用于读等待写
readerCount int32 // 当前执行读的 goroutine 数量
readerWait int32 // 被阻塞的准备读的 goroutine 的数量
}
  • w 是一个互斥锁(sync.Mutex 类型),用于控制对读写锁的访问。当有 goroutine 请求写锁时,它将会被锁住。
  • readerSem 是一个信号量(sync.Cond 类型),用于控制对共享资源的读取。它跟踪当前读取共享资源的 goroutine 数量。

读写锁的实现原理

当有 goroutine 请求读取锁时,它会首先检查 w 字段。如果该字段已被锁住(表示有其他 goroutine 正在写入),则该 goroutine 将被阻塞,直到锁释放。

如果 w 字段未被锁住,则将 readerSem 字段的内部计数器加 1,表示当前有一个读取 goroutine,然后该 goroutine 获得读取锁。

当有 goroutine 请求写入锁时,它首先会获取 w 字段的互斥锁。如果有其他 goroutine 正在读取或写入,则该 goroutine 会被阻塞,直到锁释放。一旦获取了写入锁,该 goroutine 就可以执行写入操作。

当读取锁的 goroutine 完成读取操作后,它会将 readerSem 字段的内部计数器减 1,表示当前读取的 goroutine 减少一个。

如果该计数器变为 0,而且没有其他 goroutine 正在等待写入锁,则它会释放写入锁,允许等待写入锁的 goroutine 获取它。

这种实现方式允许多个 goroutine 同时读取共享资源,提高了并发性能,但同时保证了对共享资源的写入操作是互斥的,确保数据的一致性和可靠性。