可能原因
- 读锁优先级高:RWMutex读锁允许多个读操作同时进行,而写锁需要独占资源。在高并发读的情况下,读锁不断被获取,导致写锁长时间无法获取到资源,从而产生写操作饥饿。
解决方案
- 限制读锁数量
- 思路:通过使用一个计数器来限制同时活跃的读操作数量,当读操作达到一定数量后,新的读操作将被阻塞,从而给写操作更多获取锁的机会。
- 示例代码:
package main
import (
"fmt"
"sync"
"time"
)
const maxReaders = 10
var (
rwMutex sync.RWMutex
readerCount int
readerSem = make(chan struct{}, maxReaders)
)
func read(id int) {
readerSem <- struct{}{}
rwMutex.RLock()
defer func() {
rwMutex.RUnlock()
<-readerSem
}()
fmt.Printf("Reader %d is reading\n", id)
time.Sleep(time.Millisecond * 100)
}
func write(id int) {
rwMutex.Lock()
defer rwMutex.Unlock()
fmt.Printf("Writer %d is writing\n", id)
time.Sleep(time.Millisecond * 100)
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 20; i++ {
if i%5 == 0 {
wg.Add(1)
go func(id int) {
defer wg.Done()
write(id)
}(i)
} else {
wg.Add(1)
go func(id int) {
defer wg.Done()
read(id)
}(i)
}
}
wg.Wait()
}
- 公平锁机制
- 思路:引入一个公平队列,按照请求顺序来处理锁的获取,保证写操作不会因为读操作的高并发而被无限期推迟。可以使用一个channel来模拟公平队列。
- 示例代码:
package main
import (
"fmt"
"sync"
"time"
)
type FairRWMutex struct {
rwMutex sync.RWMutex
waitQueue chan struct{}
isWriting bool
}
func NewFairRWMutex() *FairRWMutex {
return &FairRWMutex{
waitQueue: make(chan struct{}, 1),
}
}
func (frw *FairRWMutex) RLock() {
for {
if frw.isWriting {
<-frw.waitQueue
} else {
break
}
}
frw.rwMutex.RLock()
frw.waitQueue <- struct{}{}
}
func (frw *FairRWMutex) RUnlock() {
frw.rwMutex.RUnlock()
<-frw.waitQueue
}
func (frw *FairRWMutex) Lock() {
frw.waitQueue <- struct{}{}
frw.isWriting = true
frw.rwMutex.Lock()
}
func (frw *FairRWMutex) Unlock() {
frw.rwMutex.Unlock()
frw.isWriting = false
<-frw.waitQueue
}
func read(id int, frw *FairRWMutex) {
frw.RLock()
defer frw.RUnlock()
fmt.Printf("Reader %d is reading\n", id)
time.Sleep(time.Millisecond * 100)
}
func write(id int, frw *FairRWMutex) {
frw.Lock()
defer frw.Unlock()
fmt.Printf("Writer %d is writing\n", id)
time.Sleep(time.Millisecond * 100)
}
func main() {
frw := NewFairRWMutex()
var wg sync.WaitGroup
for i := 0; i < 20; i++ {
if i%5 == 0 {
wg.Add(1)
go func(id int) {
defer wg.Done()
write(id, frw)
}(i)
} else {
wg.Add(1)
go func(id int) {
defer wg.Done()
read(id, frw)
}(i)
}
}
wg.Wait()
}