MST

星途 面试题库

面试题:Go语言RWMutex读优先级实现中饥饿问题探讨

Go语言的RWMutex在保证读优先级的同时,可能会引入写操作的饥饿问题。请详细分析这种饥饿问题是如何产生的,以及在实际应用中,你会采取哪些策略来缓解或解决这个问题?
38.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

1. 饥饿问题产生原因

  • 读锁特性RWMutex允许多个读操作同时进行,读锁不互斥。当有读操作在进行时,新的读操作可以不断获取读锁。
  • 写锁互斥:写锁与读锁、写锁与写锁之间是互斥的。如果在写操作等待获取锁的过程中,不断有新的读操作到来并获取读锁,写操作就会一直处于等待状态,从而导致写操作饥饿。 例如,假设当前有一个写操作W1等待获取锁,此时陆续来了读操作R1R2R3等,由于读锁可以并发获取,这些读操作会依次获取读锁并执行,而W1只能一直等待,若读操作源源不断,W1就会长时间无法获取锁。

2. 缓解或解决策略

  • 公平锁策略
    • 原理:实现一个公平的锁机制,按照请求锁的先后顺序来分配锁。可以通过一个队列来记录请求锁的顺序,当锁可用时,按照队列顺序依次分配。
    • 示例代码
package main

import (
    "fmt"
    "sync"
)

type FairRWMutex struct {
    mu       sync.Mutex
    readers  int
    writers  int
    waiters  int
    readerCh chan struct{}
    writerCh chan struct{}
}

func NewFairRWMutex() *FairRWMutex {
    return &FairRWMutex{
        readerCh: make(chan struct{}, 1),
        writerCh: make(chan struct{}, 1),
    }
}

func (rw *FairRWMutex) RLock() {
    rw.mu.Lock()
    if rw.writers > 0 || rw.waiters > 0 {
        rw.waiters++
        rw.mu.Unlock()
        <-rw.readerCh
        rw.mu.Lock()
        rw.waiters--
    }
    rw.readers++
    rw.mu.Unlock()
}

func (rw *FairRWMutex) RUnlock() {
    rw.mu.Lock()
    rw.readers--
    if rw.readers == 0 && rw.waiters > 0 {
        select {
        case rw.writerCh <- struct{}{}:
        default:
        }
    }
    rw.mu.Unlock()
}

func (rw *FairRWMutex) Lock() {
    rw.mu.Lock()
    if rw.readers > 0 || rw.writers > 0 {
        rw.waiters++
        rw.mu.Unlock()
        <-rw.writerCh
        rw.mu.Lock()
        rw.waiters--
    }
    rw.writers++
    rw.mu.Unlock()
}

func (rw *FairRWMutex) Unlock() {
    rw.mu.Lock()
    rw.writers--
    if rw.waiters > 0 {
        select {
        case rw.writerCh <- struct{}{}:
        default:
            select {
            case rw.readerCh <- struct{}{}:
            default:
            }
        }
    }
    rw.mu.Unlock()
}
  • 写优先策略
    • 原理:在每次读操作获取锁之前,检查是否有写操作在等待。如果有写操作等待,则读操作等待,优先让写操作获取锁。
    • 示例代码
package main

import (
    "fmt"
    "sync"
)

type WritePreferRWMutex struct {
    mu       sync.Mutex
    readers  int
    writers  int
    writeWait int
}

func (rw *WritePreferRWMutex) RLock() {
    rw.mu.Lock()
    for rw.writers > 0 || rw.writeWait > 0 {
        rw.mu.Unlock()
        rw.mu.Lock()
    }
    rw.readers++
    rw.mu.Unlock()
}

func (rw *WritePreferRWMutex) RUnlock() {
    rw.mu.Lock()
    rw.readers--
    rw.mu.Unlock()
}

func (rw *WritePreferRWMutex) Lock() {
    rw.mu.Lock()
    rw.writeWait++
    for rw.readers > 0 || rw.writers > 0 {
        rw.mu.Unlock()
        rw.mu.Lock()
    }
    rw.writers++
    rw.writeWait--
    rw.mu.Unlock()
}

func (rw *WritePreferRWMutex) Unlock() {
    rw.mu.Lock()
    rw.writers--
    rw.mu.Unlock()
}
  • 定期提升写操作优先级
    • 原理:设置一个定时器或者计数器,当写操作等待时间超过一定阈值或者写操作等待次数达到一定数量时,强制暂停读操作,优先让写操作获取锁。
    • 示例:在一个服务中,记录写操作等待时间,若超过100毫秒,在下次锁释放时,优先分配给写操作。可以通过在锁的获取和释放逻辑中添加时间记录和判断逻辑来实现。