Go-读者写者问题
参考小林的讲解,写了一版Go的,方便自己复习,如果有错误欢迎指正
方案一:读者优先
写者有可能一直处于饥饿状态
package main
import (
"fmt"
"sync"
)
var (
wg sync.WaitGroup
rCount int //读者数量
rCountMutex sync.Mutex //读者数量的锁
wMutex sync.Mutex //写锁
)
var num = 100
func write() {
num++
fmt.Println("write: num = ", num)
}
func read() {
fmt.Println("read: num = ", num)
}
func reader() {
defer wg.Done()
rCountMutex.Lock() //要修改读者数量时,对读者数量操作加锁
if rCount == 0 {
wMutex.Lock() //如果是读者第一次进入,则要对写加锁,防止后面的写者进入
}
rCount++ //读者数量加1
rCountMutex.Unlock() //读者数量正确加1,可以解锁
read() //执行读操作
rCountMutex.Lock()
rCount--
if rCount == 0 {
wMutex.Unlock() //如果此时没有读者了,要唤醒写者
}
rCountMutex.Unlock()
}
func writer() {
defer wg.Done()
wMutex.Lock()
write()
wMutex.Unlock()
}
func test() {
for i := 0; i < 10; i++ {
wg.Add(1)
go reader()
}
for i := 0; i < 10; i++ {
wg.Add(1)
go writer()
}
wg.Wait()
}
func main() {
test()
}
/*
read: num = 100
read: num = 100
read: num = 100
read: num = 100
read: num = 100
read: num = 100
read: num = 100
read: num = 100
read: num = 100
write: num = 101
write: num = 102
write: num = 103
write: num = 104
write: num = 105
write: num = 106
write: num = 107
write: num = 108
write: num = 109
write: num = 110
read: num = 110
*/
方案二:写者优先
读者有可能一直处于饥饿状态
package main
import (
"fmt"
"sync"
)
var (
wg sync.WaitGroup
rCount int //记录读者数量
rCountMutex sync.Mutex //读者数量的锁
rMutex sync.Mutex //读操作的锁
wCount int //记录写者数量
wCountMutex sync.Mutex //写者数量的锁
wMutex sync.Mutex //写操作的锁
)
var num = 100
func write() {
num++
fmt.Println("write: num = ", num)
}
func read() {
fmt.Println("read: num = ", num)
}
func writer() {
defer wg.Done()
wCountMutex.Lock()
if wCount == 0 {
rMutex.Lock() //对读操作加锁,要进行写操作,不能让读者进来
}
wCount++
wCountMutex.Unlock()
wMutex.Lock() //执行写操作,加锁
write()
wMutex.Unlock()
wCountMutex.Lock()
wCount--
if wCount == 0 {
rMutex.Unlock() //此时没有写者了,唤醒读者
}
wCountMutex.Unlock()
}
func reader() {
defer wg.Done()
rMutex.Lock() //用来判断此时是否有写者,如果有写者,会阻塞在这里
rCountMutex.Lock()
if rCount == 0 {
wMutex.Lock()
}
rCount++
rCountMutex.Unlock()
rMutex.Unlock()
read() //执行读操作
rCountMutex.Lock()
rCount--
if rCount == 0 {
wMutex.Unlock()
}
rCountMutex.Unlock()
}
func test() {
for i := 0; i < 10; i++ {
wg.Add(1)
go reader()
}
for i := 0; i < 10; i++ {
wg.Add(1)
go writer()
}
wg.Wait()
}
func main() {
test()
}
/*
read: num = 100
write: num = 101
write: num = 102
write: num = 103
write: num = 104
write: num = 105
write: num = 106
write: num = 107
write: num = 108
write: num = 109
write: num = 110
read: num = 110
read: num = 110
read: num = 110
read: num = 110
read: num = 110
read: num = 110
read: num = 110
read: num = 110
read: num = 110
*/
方案三:公平策略
- 优先级相同
- 写者、读者互斥访问
- 只能一个写者访问临界区
- 可以有多个读者同时访问临界区
为什么相比于读者优先策略,仅多出一个flag锁,就可以保证公平呢?
读者优先策略中,只要读者数没有到0,哪怕有新的读者在写者后到达,也可以直接加入到读者队列进行读操作,写者只能一直等待。
增加flag锁之后,先来的读者进入读者队列后释放了flag锁,然后写者到达抢到flag锁,因为此时读者队列中还有读者,所以写者会阻塞;新的读者到达后,因为flag锁已经被写者占据,所以无法进入到读者队列中,直到最开始到达的所有读者全部读完,唤醒写者,才可以继续流程。
package main
import (
"fmt"
"sync"
)
var (
wg sync.WaitGroup
rCount int
rCountMutex sync.Mutex //读者数量锁
wMutex sync.Mutex //写操作锁
flag sync.Mutex //为了保证读写公平读锁
)
var num = 100
func write() {
num++
fmt.Println("write: num = ", num)
}
func read() {
fmt.Println("read: num = ", num)
}
func writer() {
defer wg.Done()
flag.Lock()
wMutex.Lock()
write()
wMutex.Unlock()
flag.Unlock()
}
func reader() {
defer wg.Done()
flag.Lock()
rCountMutex.Lock()
if rCount == 0 {
wMutex.Lock()
}
rCount++
rCountMutex.Unlock()
flag.Unlock()
read()
rCountMutex.Lock()
rCount--
if rCount == 0 {
wMutex.Unlock()
}
rCountMutex.Unlock()
}
func test() {
for i := 0; i < 10; i++ {
wg.Add(1)
go reader()
}
for i := 0; i < 10; i++ {
wg.Add(1)
go writer()
}
wg.Wait()
}
func main() {
test()
}
/*
read: num = 100
read: num = 100
write: num = 101
read: num = 101
read: num = 101
read: num = 101
write: num = 102
write: num = 103
write: num = 104
write: num = 105
write: num = 106
write: num = 107
write: num = 108
write: num = 109
write: num = 110
read: num = 110
read: num = 110
read: num = 110
read: num = 110
read: num = 110
*/