MST

星途 面试题库

面试题:Go并发编程中如何避免读写互斥锁导致的饥饿问题?

在使用Go的sync.RWMutex时,可能会出现写操作饥饿的情况,即写操作长时间得不到执行。请分析产生这种饥饿问题的原因,并提出至少两种有效的解决方案,要求结合代码示例说明每种方案是如何解决该问题的。
16.6万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

原因分析

sync.RWMutex设计初衷是为了在读多写少场景下提升性能。读锁可以被多个读操作同时持有,而写锁则是独占的。当有大量读操作不断获取读锁时,写操作由于需要独占锁,就可能会长时间无法获取到锁,从而导致写操作饥饿。

解决方案及代码示例

  1. 公平锁机制
    • 思路:通过维护一个等待队列,按照请求顺序分配锁,使得写操作能按照顺序获取锁,避免饥饿。
    • 代码示例
package main

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

type FairMutex struct {
    sync.Mutex
    readers  int
    writers  int
    writerCh chan struct{}
}

func NewFairMutex() *FairMutex {
    return &FairMutex{
        writerCh: make(chan struct{}, 1),
    }
}

func (fm *FairMutex) RLock() {
    fm.Lock()
    for fm.writers > 0 {
        fm.Unlock()
        time.Sleep(time.Millisecond)
        fm.Lock()
    }
    fm.readers++
    fm.Unlock()
}

func (fm *FairMutex) RUnlock() {
    fm.Lock()
    fm.readers--
    if fm.readers == 0 && len(fm.writerCh) > 0 {
        close(fm.writerCh)
    }
    fm.Unlock()
}

func (fm *FairMutex) Lock() {
    fm.Lock()
    fm.writers++
    for fm.readers > 0 || len(fm.writerCh) > 0 {
        fm.Unlock()
        fm.writerCh <- struct{}{}
        fm.Lock()
    }
}

func (fm *FairMutex) Unlock() {
    fm.writers--
    if fm.writers == 0 && len(fm.writerCh) > 0 {
        close(fm.writerCh)
    }
    fm.Unlock()
}
  1. 限制读操作数量
    • 思路:设定一个最大读操作数量,当读操作达到这个数量后,不再允许新的读操作获取读锁,从而给写操作机会。
    • 代码示例
package main

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

type LimitingMutex struct {
    sync.RWMutex
    maxReaders int
    currentReaders int
}

func NewLimitingMutex(max int) *LimitingMutex {
    return &LimitingMutex{
        maxReaders: max,
    }
}

func (lm *LimitingMutex) RLock() {
    for {
        lm.RLock()
        if lm.currentReaders < lm.maxReaders {
            lm.currentReaders++
            lm.RUnlock()
            break
        }
        lm.RUnlock()
        time.Sleep(time.Millisecond)
    }
}

func (lm *LimitingMutex) RUnlock() {
    lm.RLock()
    lm.currentReaders--
    lm.RUnlock()
}