23.2 读写锁

读写锁是分别针对读操作和写操作进行锁定和解锁操作的互斥锁。

在Go语言中,读写锁由结构体类型 sync.RWMutex 代表。基本遵循原则:

  • 写锁定情况下,对读写锁进行读锁定或者写锁定,都将阻塞;而且读锁与写锁之间是互斥的;
  • 读锁定情况下,对读写锁进行写锁定,将阻塞;加读锁时不会阻塞;
  • 对未被写锁定的读写锁进行写解锁,会引发panic;
  • 对未被读锁定的读写锁进行读解锁的时候也会引发panic;
  • 写解锁在进行的同时会试图唤醒所有因进行读锁定而被阻塞的协程;
  • 读解锁在进行的时候则会试图唤醒一个因进行写锁定而被阻塞的协程。

与互斥锁类似,sync.RWMutex类型的零值就已经是立即可用的读写锁了。在此类型的方法集合中包含了两对方法,即:RWMutex提供四个方法:

func (*RWMutex) Lock // 写锁定
func (*RWMutex) Unlock // 写解锁
func (*RWMutex) RLock // 读锁定
func (*RWMutex) RUnlock // 读解锁
package main
import (
	"fmt"
	"sync"
	"time"
)
var m *sync.RWMutex
func main() {
	wg := sync.WaitGroup{}
	wg.Add(20)
	var rwMutex sync.RWMutex
	Data := 0
	for i := 0; i < 10; i++ {
		go func(t int) {
			rwMutex.RLock()
			defer rwMutex.RUnlock()
			fmt.Printf("Read data: %v\n", Data)
			wg.Done()
			time.Sleep(2 * time.Second)
			// 这句代码第一次运行后,读解锁。
			// 循环到第二个时,读锁定后,这个协程就没有阻塞,同时读成功。
		}(i)
		go func(t int) {
			rwMutex.Lock()
			defer rwMutex.Unlock()
			Data += t
			fmt.Printf("Write Data: %v %d \n", Data, t)
			wg.Done() 
			// 这句代码让写锁的效果显示出来,写锁定下是需要解锁后才能写的。
			time.Sleep(2 * time.Second)
		}(i)
	}
	time.Sleep(5 * time.Second)
	wg.Wait()
}