Go微服务实战
上QQ阅读APP看书,第一时间看更新

8.2.2 sync.RWMutex多读写锁

sync.RWMutex允许读操作并行执行,而写操作仍然是互斥的,通过这种方式可以提高效率。

sync.RWMutex锁是基于sync.Mutex的增强,下面来看一下其结构体:


type RWMutex struct{
    w           Mutex
    writerSem   uint32
    readerSem   uint32
    readerCount int32
    readerWait  int32
}

因为sync.RWMutex是对sync.Mutex的增强,而且在其结构体中也确实包含sync.Mutex,所以Mutex的锁还是可以用的,只是RWMutex又多了对读操作的锁定和解锁的操作。

sync.RWMutex除了实现Locker接口以外,还实现了Unlock、RLock和RUnlock接口,下面来看一下RWMutex的四个方法:


func (rw *RWMutex) Lock()
func (rw *RWMutex) Unlock()

func (rw *RWMutex) RLock()
func (rw *RWMutex) RUnlock()

前面两个分别是对写操作的锁定和解锁;后面两个是对读操作的锁定和解锁。RWMutex锁在使用的时候有如下要求:

▪一次只能有一个goroutine获取写锁。

▪可以有多个goroutine同时获取读锁。

▪只能同时获取读锁或者写锁,读和写是互斥的。

在8.2.1节详细介绍了sync.Mutex,所以此处介绍RWMutex就比较简单了,只需看下面的示例:


book/ch08/8.2/RWMutex/rwmutex.go
1. package rwmutex
2.
3. import (
4.     "fmt"
5.     "sync"
6.     "time"
7. )
8.
9. type pass struct {
10.     RWM sync.RWMutex
11.     pwd string
12. }
13. var RoomPass = pass{pwd:"initPass"}
14.
15. func Change(p *pass,pwd string)  {
16.     p.RWM.Lock()
17.     fmt.Println()
18.     time.Sleep(5*time.Second)
19.     p.pwd = pwd
20.     p.RWM.Unlock()
21. }
22.
23. func getPWD(p *pass) string {
24.     p.RWM.RLock()
25.     fmt.Println("read pwd")
26.     time.Sleep(time.Second)
27.     defer p.RWM.RUnlock()
28.     return p.pwd
29. }

这里定义一个密码的结构体,里面包含RWMutex锁和一个字符串类型的pwd变量,它们用来记录密码。一般情况下,房间的密码是可以多人读取的,毕竟这个房间可能住了多个人,所以这里使用RWMutex锁。

这时使用了第23行至第29行的getPWD方法。该方法内使用了RWMutex锁,是允许多个goroutine并行读取的。而在第15行至第21行的Change方法用于修改房间密码。注意,只要有人在读,Change方法就会阻塞;同样地,若有人在写,getPWD方法也会阻塞。

这个简单的例子就是对RWMutex的使用,接下来看一下sync.Once锁。