MST

星途 面试题库

面试题:Go语言RWMutex锁的读写锁竞争处理

在Go语言中,当多个读操作和写操作同时竞争RWMutex锁时,会发生什么情况?如何避免写操作长时间被读操作阻塞?请给出具体的代码示例及解释。
49.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

1. 多个读操作和写操作同时竞争RWMutex锁时的情况

  • 读操作优先:Go语言的 RWMutex 设计为读操作优先。当有读操作正在进行时,写操作会被阻塞,直到所有读操作完成。如果在写操作等待时,又有新的读操作到来,新的读操作会被允许执行,这就可能导致写操作长时间被读操作阻塞。

2. 避免写操作长时间被读操作阻塞的方法

  • 使用公平锁:可以通过自定义一个机制,来实现类似公平锁的效果,让写操作有机会优先执行。例如,记录写操作的等待时间,当等待时间超过一定阈值时,优先处理写操作。另一种方法是在写操作较多的场景下,尽量减少读操作的粒度,避免读操作长时间占用锁。

3. 代码示例及解释

package main

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

// 定义一个带公平机制的读写锁
type FairRWMutex struct {
    sync.RWMutex
    writeWaiters int
    writeCh      chan struct{}
}

// 自定义的写锁获取方法
func (rw *FairRWMutex) FairLock() {
    rw.Lock()
    defer rw.Unlock()
    rw.writeWaiters++
    go func() {
        rw.writeCh <- struct{}{}
    }()
    for rw.writeWaiters > 0 {
        <-rw.writeCh
    }
}

// 自定义的写锁释放方法
func (rw *FairRWMutex) FairUnlock() {
    rw.Lock()
    defer rw.Unlock()
    rw.writeWaiters--
    if rw.writeWaiters == 0 {
        close(rw.writeCh)
        rw.writeCh = make(chan struct{})
    }
}

func main() {
    var wg sync.WaitGroup
    fairRW := FairRWMutex{writeCh: make(chan struct{})}

    // 模拟读操作
    for i := 0; i < 3; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            fairRW.RLock()
            fmt.Printf("Reader %d is reading\n", id)
            time.Sleep(time.Second)
            fairRW.RUnlock()
        }(i)
    }

    // 模拟写操作
    wg.Add(1)
    go func() {
        defer wg.Done()
        fairRW.FairLock()
        fmt.Println("Writer is writing")
        time.Sleep(time.Second)
        fairRW.FairUnlock()
    }()

    wg.Wait()
}

代码解释

  • FairRWMutex结构体:包含一个标准的 sync.RWMutex,同时增加了 writeWaiters 用于记录写操作等待的数量,writeCh 用于控制写操作的执行顺序。
  • FairLock方法:在获取写锁时,先调用标准的 Lock 方法,然后增加 writeWaiters 计数,并向 writeCh 发送信号。之后通过循环等待,直到 writeWaiters 变为0,确保写操作能够优先执行。
  • FairUnlock方法:在释放写锁时,减少 writeWaiters 计数,当计数为0时,关闭并重新创建 writeCh 通道。
  • main函数:启动多个读操作和一个写操作,通过上述自定义的公平锁机制,保证写操作不会被读操作长时间阻塞。