写给自己的记录,最近在看锁的一些底层实现,发现读写锁和我之前的理解有一点点偏差,在这里写一些自己的心得,方便以后查阅。
之前了解过golang中的最简单的互斥锁,就是很正常的加锁,访问资源,访问完毕,释放锁的整个过程,读写锁就是可以多个goroutine同时加RLock,但是如果之前有一个goroutine加了写锁就不能调用RLock call了。我写了一个简单的程序来展示一下。
import (
"fmt"
"sync"
"time"
)
var lock sync.RWMutex
var sum int = 1
var wg sync.WaitGroup
// 模拟一个简单的写goroutine,加写锁的那种
func write(i int) {
lock.Lock()
fmt.Printf("the %d write..\n ", i)
sum += 1
fmt.Printf("the %d write done, the num is %d \n", i, sum)
lock.Unlock()
wg.Done()
}
// 模拟一个简单的读goroutine,加读锁的那种
func read(i int) {
lock.RLock()
fmt.Printf("the %d read thread read the num is %d\n", i, sum)
time.Sleep(time.Duration(i) * time.Second)
lock.RUnlock()
fmt.Printf("the %d thread free the read lock\n", i)
wg.Done()
}
// 模拟一个简单的普通的读goroutine,不加锁的那种
func rawread(i int) {
fmt.Printf("the raw %d read %d\n", i, sum)
wg.Done()
}
func main() {
// 开启10个写goroutine
for i := 0; i < 10; i++ {
wg.Add(1)
go write(i)
}
//开启10个加锁的读goroutine,并且后启动的读的时间延长
for j := 0; j < 10; j++ {
wg.Add(1)
go read(j)
}
//开启十个普通的读goroutine
for k := 0; k < 10; k++ {
wg.Add(1)
go rawread(k)
}
wg.Wait()
}
最后的结果:
the 0 write..
the 0 write done, the num is 2
the 0 read thread read the num is 2
the 0 thread free the read lock
the 1 read thread read the num is 2
the 4 read thread read the num is 2
the raw 9 read 2
the 8 read thread read the num is 2
the 2 read thread read the num is 2
the 9 read thread read the num is 2
the 6 read thread read the num is 2
the raw 1 read 2
the raw 0 read 2
the 3 read thread read the num is 2
the raw 2 read 2
the raw 3 read 2
the raw 4 read 2
the raw 5 read 2
the raw 6 read 2
the raw 7 read 2
the raw 8 read 2
the 1 thread free the read lock
the 2 thread free the read lock
the 3 thread free the read lock
the 4 thread free the read lock
the 6 thread free the read lock
the 8 thread free the read lock
the 9 thread free the read lock
the 1 write..
the 1 write done, the num is 3
the 5 read thread read the num is 3
the 7 read thread read the num is 3
the 5 thread free the read lock
the 7 thread free the read lock
the 3 write..
the 3 write done, the num is 4
the 2 write..
the 2 write done, the num is 5
the 5 write..
the 5 write done, the num is 6
the 4 write..
the 4 write done, the num is 7
the 7 write..
the 7 write done, the num is 8
the 6 write..
the 6 write done, the num is 9
the 8 write..
the 8 write done, the num is 10
the 9 write..
the 9 write done, the num is 11
Process finished with exit code 0
可以看出 在很多加锁的读协程在读而且没有释放读锁的时候,很多普通的或者加锁的读goroutine也可以读取或者加读锁读取,而写协程就不可以。这一点就是我之前理解有点偏差的地方,在golang的底层是通过原子操作来保证这种读写锁的性质的,再往下就是使用汇编里面的操作。读写锁所支持的最多的reader为 rwmutexMaxReaders = 1 << 30。