MST

星途 面试题库

面试题:Go语言中自定义一个能彻底解决RWMutex写饥饿问题的读写锁实现

在Go语言标准库的RWMutex可能存在写饥饿问题的基础上,要求你自定义一个读写锁结构及其方法,确保在高并发读写场景下不会出现写饥饿现象,给出完整的代码实现,并对关键部分进行详细注释。
35.6万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

import (
    "fmt"
    "sync"
    "time"
)

// 自定义读写锁结构体
type MyRWMutex struct {
    mu        sync.Mutex
    readers   int
    writers   int
    writeCond *sync.Cond
    readCond  *sync.Cond
}

// 初始化自定义读写锁
func NewMyRWMutex() *MyRWMutex {
    m := &MyRWMutex{}
    m.writeCond = sync.NewCond(&m.mu)
    m.readCond = sync.NewCond(&m.mu)
    return m
}

// 读锁定
func (rw *MyRWMutex) RLock() {
    rw.mu.Lock()
    for rw.writers > 0 { // 如果有写操作正在进行或等待,读操作等待
        rw.readCond.Wait()
    }
    rw.readers++
    rw.mu.Unlock()
}

// 读解锁
func (rw *MyRWMutex) RUnlock() {
    rw.mu.Lock()
    rw.readers--
    if rw.readers == 0 { // 如果没有读操作了,唤醒等待的写操作
        rw.writeCond.Broadcast()
    }
    rw.mu.Unlock()
}

// 写锁定
func (rw *MyRWMutex) Lock() {
    rw.mu.Lock()
    rw.writers++
    for rw.readers > 0 || rw.writers > 1 { // 如果有读操作正在进行或者有其他写操作正在等待,写操作等待
        rw.writeCond.Wait()
    }
    rw.mu.Unlock()
}

// 写解锁
func (rw *MyRWMutex) Unlock() {
    rw.mu.Lock()
    rw.writers--
    if rw.writers == 0 { // 如果没有写操作了,唤醒等待的读操作和写操作
        rw.writeCond.Broadcast()
        rw.readCond.Broadcast()
    }
    rw.mu.Unlock()
}

在这个实现中:

  1. MyRWMutex 结构体包含了一个基础的互斥锁 mu,用于保护读写状态的修改。
  2. readers 记录当前正在进行的读操作数量,writers 记录当前正在进行的写操作数量或等待写操作的数量。
  3. writeCondreadCond 分别是用于写操作和读操作等待与唤醒的条件变量。

在读锁定 RLock 方法中,当有写操作正在进行或等待时,读操作会等待,直到没有写操作。读解锁 RUnlock 方法中,当所有读操作结束,会唤醒等待的写操作。

在写锁定 Lock 方法中,当有读操作正在进行或者有其他写操作正在等待时,写操作会等待。写解锁 Unlock 方法中,当写操作结束,会唤醒所有等待的读操作和写操作。这样确保了写操作不会饥饿。

下面是一个简单的测试示例:

func main() {
    var wg sync.WaitGroup
    rw := NewMyRWMutex()

    // 启动读操作
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            rw.RLock()
            fmt.Printf("Reader %d is reading\n", id)
            time.Sleep(100 * time.Millisecond)
            rw.RUnlock()
        }(i)
    }

    // 启动写操作
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            rw.Lock()
            fmt.Printf("Writer %d is writing\n", id)
            time.Sleep(200 * time.Millisecond)
            rw.Unlock()
        }(i)
    }

    wg.Wait()
}

在这个测试示例中,启动了多个读操作和写操作,以模拟高并发读写场景,验证自定义读写锁是否能避免写饥饿。