MST

星途 面试题库

面试题:Go语言RWMutex锁在高并发环境下的性能优化

在一个高并发的Go程序中,频繁地进行读写操作。已知读操作远多于写操作,现使用RWMutex锁来保护共享资源。但随着并发量增加,出现了写操作饥饿的现象。请分析可能的原因,并提出至少两种解决方案,以确保写操作在高并发读的情况下也能及时执行,同时保证整体的性能不受太大影响。
19.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能原因

  1. 读锁优先级高:RWMutex读锁允许多个读操作同时进行,而写锁需要独占资源。在高并发读的情况下,读锁不断被获取,导致写锁长时间无法获取到资源,从而产生写操作饥饿。

解决方案

  1. 限制读锁数量
    • 思路:通过使用一个计数器来限制同时活跃的读操作数量,当读操作达到一定数量后,新的读操作将被阻塞,从而给写操作更多获取锁的机会。
    • 示例代码
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()
}
  1. 公平锁机制
    • 思路:引入一个公平队列,按照请求顺序来处理锁的获取,保证写操作不会因为读操作的高并发而被无限期推迟。可以使用一个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()
}