写给自己的记录,最近在看锁的一些底层实现,发现读写锁和我之前的理解有一点点偏差,在这里写一些自己的心得,方便以后查阅。

之前了解过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。