读写锁
# 一、sync
包 --- 读写锁
- https://golang.google.cn/pkg/sync/#RWMutex
官方文件sync
的介绍
Package sync provides basic synchronization primitives such as mutual exclusion locks. Other than the Once and WaitGroup types, most are intended for use by low-level library routines. Higher-level synchronization is better done via channels and communication
sync
是synchronization
同步这个词的缩写,所以也会叫做同步包.这里提供了基本同步的操作,比如互斥锁等等.这里除了Once
和WaitGroup
类型之外,大多数类型都是供低级库例教程使用的.更高级别同步最好通过channel
通道和communication
通信来完成
# 二、RWMutex
(读写锁)
Go
语言包中的sync
包提供了两种类型: sync.Mutex
和sync.RWMutex
.其中RWMutex
基于Mutex
实现的,只读锁的实现使用类似引用计数器的功能.
RWMutex
是读/写互斥锁.锁可以由任意数量的读取器或单个编写器持有.RWMutex
的零值是未锁的mutex
.
如果一个goroutine
持有有一个rRWMutex
进行读取,而另一个goroutine
可能调用lock
,那么在释放初始读取锁之前,任何goroutine
都不应该期望能够读取锁.特别是,这禁止递归读取锁定.这是为了确保锁最终可用;被阻止的锁调用会将新的读卡器排除在获取锁之外.
怎么理解读写锁呢?当有一个goroutine
获得写锁定,其他无论是读锁还是写锁都将阻塞直到写解锁;当有一个goroutine
获得读锁定,其读锁定仍然可以继续;当一个或任意多个读锁定,写锁定将等待所有读锁解锁之后才能够进行写锁定.所以说这里的读锁定(RLock
)目的其实是告诉锁定: 有很多人在读取数据,需要靠边等待,等它们读(读解锁)完再来写(写锁定).可以将其总结为如下三条:
- 1.同时只能有一个
goroutine
能够获得写锁定. - 2.同时可以有任意多个
gorontine
获得读锁定 - 3.同时只能存在写锁定或读锁定(读和写互斥).
所以,RWMutex
写个读写锁,该锁可以加多个读锁或者一个写锁,其经常用于读写次数远远多于写次数的场景.
基本遵循两个原则:
- 1.可以随便读,多个
goroutine
同时读. - 2.写的时候,啥也不能干.不能读也不能写.
读写锁即是对于读写操作的互斥锁.它有普通的互斥锁最大的不同就是,它可以分别针对读操作和写操作进行锁定和解锁操作.读写锁遵循的访问控制规则与互斥锁有所不同.在读写锁管辖的范围内,它允许任意个读操作的同时进行.但是同一时刻,它只允许有一个写操作在进行.
并且在某一个写操作被进行的过程中,读操作的进行也是不被允许的.也就是说读写锁控制下的多个写操作之间是互斥的,并且写操作与读操作之间也都是互斥的.但是,多个读操作之间却存在互斥关系.
# 三、常用方法
# RLock()
方法
func (rw *RWMutex) RLock()
读锁,当有写锁时,无加载读锁,当只有读锁或者没有锁时,可以加载读锁,读锁可以加载多个,所以适用于"读多写少"的场景.
# RUnlock()
方法
func(rw *RWMutex) RUnlock()
读锁解锁,RUnlock
撤销单次RLock
调用,它对于其他同时存在的读取器则没有效果,若rw
并没有为读取而锁定,调用RUnlock
就会引发一个运行时的错误.
# Lock()
方法
func (rw *RWMutex) Lock()
写锁,如果在添加写锁之前已经有其他的读锁和写锁,则Lock
就会阻塞直到该锁可用,为确保该锁最终可用,已阻塞的Lock
调用会从获得的锁中排除新的读取锁,即写锁权限高于读锁,有写锁时优先进行写锁定.
# Unlock()
方法
func (rw *RWMutex) Unlock()
写锁解锁,如果没有进行写锁定,则就会引起一个运行时错误.
# 四、示例代码
package main
import (
"fmt"
"sync"
"time"
)
var rwMutex *sync.RWMutex
var wg *sync.WaitGroup
func main() {
rwMutex = new(sync.RWMutex)
wg = new (sync.WaitGroup)
//wg.Add(2)
//
////多个同时读取
//go readData(1)
//go readData(2)
wg.Add(3)
go writeData(1)
go readData(2)
go writeData(3)
wg.Wait()
fmt.Println("main..over...")
}
func writeData(i int){
defer wg.Done()
fmt.Println(i,"开始写:write start。。")
rwMutex.Lock()//写操作上锁
fmt.Println(i,"正在写:writing。。。。")
time.Sleep(3*time.Second)
rwMutex.Unlock()
fmt.Println(i,"写结束:write over。。")
}
func readData(i int) {
defer wg.Done()
fmt.Println(i, "开始读:read start。。")
rwMutex.RLock() //读操作上锁
fmt.Println(i,"正在读取数据:reading。。。")
time.Sleep(3*time.Second)
rwMutex.RUnlock() //读操作解锁
fmt.Println(i,"读结束:read over。。。")
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
执行结果
- 01
- AWS NAT-NetWork-Firwalld配置(一)04-09
- 02
- AWS NAT-NetWork-Firwalld配置(二)04-09
- 03
- kubernetes部署minio对象存储01-18