package main
import (
"fmt"
"sync"
"time"
)
/*
1、定义:
go 中的读写锁采取的是写优先的设计,如果已经有一个 writer 在等待请求锁的话,它会阻止新来的请求锁的 reader 获取到锁,所以优先保障 writer。
当然,如果有一些 reader 已经请求了锁的话,新请求的 writer 也会等待已经存在的 reader 都释放锁之后才能获取。
2、原理:
RWMutex 包含一个 Mutex,以及四个辅助字段 writerSem、readerSem、readerCount 和 readerWait:
readerCount 是负数:writer持有锁,将后来的reader阻塞休眠
readerCount 等于0:reader都已经释放了锁,会唤醒writer,让write持有锁
readerCount 不是0:将readerCount 的值保存到readerWait,writer进入阻塞状态
3、读写锁易错
重入、递归造成死锁
写锁等待活跃的reader,活跃的reader等待新来的reader
未成对出现
4、锁容易出错
互斥: 至少一个资源是被排他性独享的,其他线程必须处于等待状态,直到资源被释放。
持有和等待:goroutine 持有一个资源,并且还在请求其它 goroutine 持有的资源,也就是咱们常说的“吃着碗里,看着锅里”的意思。
不可剥夺:资源只能由持有它的 goroutine 来释放。
环路等待:一般来说,存在一组等待进程,P={P1,P2,…,PN},P1 等待 P2 持有的资源,P2 等待 P3 持有的资源,依此类推,最后是 PN 等待 P1 持有的资源,这就形成了一个环路等待的死结。
*/
func main() {
var counter Counter
for i := 0; i < 10; i++ { // 10个reader
go func() {
for {
fmt.Println(counter.Count()) // 计数器读操作
time.Sleep(time.Millisecond)
}
}()
}
for { // 一个writer
counter.Incr() // 计数器写操作
time.Sleep(time.Second)
}
}
// 一个线程安全的计数器
type Counter struct {
sync.RWMutex
count uint64
}
// 使用写锁保护
func (c *Counter) Incr() {
c.Lock()
c.count++
c.Unlock()
}
// 使用读锁保护
func (c *Counter) Count() uint64 {
c.RLock()
defer c.RUnlock()
return c.count
}