MST

星途 面试题库

面试题:Go中sync.Mutex和sync.RWMutex的区别及适用场景

请阐述在Go语言里,sync.Mutex(互斥锁)和sync.RWMutex(读写互斥锁)的主要区别是什么,并且举例说明在哪些场景下适合使用sync.Mutex,哪些场景下适合使用sync.RWMutex。
45.7万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

主要区别

  1. 读写操作限制
    • sync.Mutex:互斥锁,同一时间只允许一个goroutine进行读写操作。无论是读操作还是写操作,都需要获取该锁,这保证了数据的一致性,但在高并发读场景下效率较低。
    • sync.RWMutex:读写互斥锁,允许多个goroutine同时进行读操作,因为读操作不会修改数据,所以不会产生数据竞争。但是写操作时,需要独占锁,不允许其他任何读或写操作,以保证数据的一致性。
  2. 适用场景
    • sync.Mutex:适用于读写操作频繁且读写操作都需要保证数据一致性,不允许并发读写的场景。
    • sync.RWMutex:适用于读操作远远多于写操作的场景,读操作并发执行可以提高效率,而写操作时确保独占以保证数据一致性。

适用场景举例

  1. 适合使用sync.Mutex的场景: 假设我们有一个银行账户结构体,每次对账户余额的修改(取款、存款操作)都需要保证数据的一致性,不允许并发操作。
package main

import (
    "fmt"
    "sync"
)

type BankAccount struct {
    balance int
    mutex   sync.Mutex
}

func (b *BankAccount) Deposit(amount int) {
    b.mutex.Lock()
    defer b.mutex.Unlock()
    b.balance += amount
}

func (b *BankAccount) Withdraw(amount int) {
    b.mutex.Lock()
    defer b.mutex.Unlock()
    if b.balance >= amount {
        b.balance -= amount
    }
}

func main() {
    account := BankAccount{balance: 100}
    var wg sync.WaitGroup
    wg.Add(2)
    go func() {
        account.Deposit(50)
        wg.Done()
    }()
    go func() {
        account.Withdraw(30)
        wg.Done()
    }()
    wg.Wait()
    fmt.Println("Final balance:", account.balance)
}

在这个例子中,无论是存款还是取款操作,都涉及对账户余额的修改,需要保证操作的原子性,所以使用sync.Mutex

  1. 适合使用sync.RWMutex的场景: 假设我们有一个缓存系统,缓存中的数据读取频率很高,而更新频率较低。
package main

import (
    "fmt"
    "sync"
)

type Cache struct {
    data map[string]interface{}
    rwMutex sync.RWMutex
}

func (c *Cache) Get(key string) interface{} {
    c.rwMutex.RLock()
    defer c.rwMutex.RUnlock()
    return c.data[key]
}

func (c *Cache) Set(key string, value interface{}) {
    c.rwMutex.Lock()
    defer c.rwMutex.Unlock()
    if c.data == nil {
        c.data = make(map[string]interface{})
    }
    c.data[key] = value
}

func main() {
    cache := Cache{}
    var wg sync.WaitGroup
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", id)
            cache.Set(key, id)
        }(i)
    }
    wg.Wait()
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", id)
            value := cache.Get(key)
            fmt.Printf("Key: %s, Value: %v\n", key, value)
        }(i)
    }
    wg.Wait()
}

在这个缓存系统中,读操作(Get方法)可以并发执行,因为读操作不会修改数据,而写操作(Set方法)需要独占锁以保证数据一致性,所以适合使用sync.RWMutex