Go-读者写者问题

文章介绍了在Go语言中解决读者写者问题的三种策略:读者优先、写者优先和公平策略。每种策略都使用了互斥锁来协调并发读写,以避免数据竞争。读者优先可能导致写者饥饿,写者优先可能导致读者饥饿,而公平策略通过额外的flag锁实现了读写公平。
摘要由CSDN通过智能技术生成

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
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值