MST
星途 面试题库

面试题:Go中Mutex与读写锁的区别及适用场景

对比Go语言里的Mutex和读写锁(`sync.RWMutex`),说明它们在实现原理、性能表现上的差异,并详细描述在什么样的读写操作场景下应该选择Mutex,什么样的场景适合使用读写锁,同时给出相关示例代码。
27.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

实现原理差异

  • Mutex(互斥锁):Mutex是一种简单的同步原语,用于保证同一时间只有一个goroutine可以访问共享资源。它通过一个状态位来表示锁的状态,当一个goroutine获取锁时,会将状态位设置为锁定状态,其他goroutine尝试获取锁时会阻塞,直到锁被释放。
  • sync.RWMutex(读写锁):读写锁区分了读操作和写操作。允许多个goroutine同时进行读操作,因为读操作不会修改共享资源,不会产生数据竞争。而写操作需要独占访问,以防止数据不一致。读写锁内部维护了两个计数器,一个用于记录当前正在进行的读操作数量,另一个用于表示是否有写操作正在进行或等待。

性能表现差异

  • Mutex:在读写操作频繁且写操作较多的场景下,Mutex会导致较高的性能开销,因为每次读写操作都需要获取锁,其他goroutine都要等待锁的释放,容易形成瓶颈。
  • sync.RWMutex:在读操作远多于写操作的场景下,读写锁能显著提高性能。由于读操作可以并发执行,减少了读操作之间的等待时间。但如果写操作频繁,写操作需要等待所有读操作完成,也可能会导致性能下降。

适用场景

  • 适合使用Mutex的场景:当读写操作频率相近,或者写操作比较频繁时,使用Mutex可以简单有效地保证数据一致性。因为读写锁在写操作时需要等待所有读操作完成,在写操作频繁的情况下,这种等待可能会导致性能问题,而Mutex直接限制同一时间只有一个操作,避免了复杂的等待逻辑。
    package main
    
    import (
        "fmt"
        "sync"
    )
    
    var (
        mu    sync.Mutex
        count int
    )
    
    func main() {
        var wg sync.WaitGroup
        for i := 0; i < 10; i++ {
            wg.Add(1)
            go func() {
                defer wg.Done()
                mu.Lock()
                count++
                fmt.Println("Incremented count:", count)
                mu.Unlock()
            }()
        }
        wg.Wait()
    }
    
  • 适合使用读写锁的场景:当读操作远远多于写操作时,使用读写锁能提高并发性能。读操作可以并行执行,只有写操作需要独占资源。
    package main
    
    import (
        "fmt"
        "sync"
    )
    
    var (
        rwmu  sync.RWMutex
        count int
    )
    
    func read(id int, wg *sync.WaitGroup) {
        defer wg.Done()
        rwmu.RLock()
        fmt.Printf("Reader %d read count: %d\n", id, count)
        rwmu.RUnlock()
    }
    
    func write(id int, wg *sync.WaitGroup) {
        defer wg.Done()
        rwmu.Lock()
        count++
        fmt.Printf("Writer %d incremented count to: %d\n", id, count)
        rwmu.Unlock()
    }
    
    func main() {
        var wg sync.WaitGroup
        for i := 0; i < 5; i++ {
            wg.Add(1)
            go read(i, &wg)
        }
        for i := 0; i < 2; i++ {
            wg.Add(1)
            go write(i, &wg)
        }
        wg.Wait()
    }