面试题答案
一键面试RWMutex读锁和写锁的使用场景
- 读锁(RLock):适用于只读取共享资源的场景,当多个goroutine同时读取共享资源时,不会造成数据不一致问题,所以允许多个读操作同时进行,这样可以提升并发读的性能。例如在一个存储配置信息的结构体,多个goroutine可能同时需要读取配置,此时使用读锁。
- 写锁(Lock):适用于修改共享资源的场景。因为写操作会改变共享资源的状态,如果多个写操作或者读写操作同时进行,会导致数据不一致的问题,所以写锁要独占,保证同一时间只有一个goroutine能进行写操作。比如在更新数据库连接池的连接配置时,需要使用写锁。
读锁允许多个读操作同时进行,写锁要独占的原因
- 读操作不改变数据:读操作只是获取数据,多个读操作同时进行不会互相影响,不会导致数据状态的不一致,所以允许多个读操作并发执行以提高效率。
- 写操作改变数据:写操作会改变共享资源的状态,如果多个写操作同时进行,会导致最终的数据状态不可预期,读写操作同时进行也可能导致读到“脏数据”,所以写操作需要独占资源,确保数据一致性。
读写频繁场景下使用RWMutex平衡性能示例
package main
import (
"fmt"
"sync"
"time"
)
var (
data int
rw sync.RWMutex
)
func read(id int, wg *sync.WaitGroup) {
defer wg.Done()
rw.RLock()
fmt.Printf("Reader %d reading data: %d\n", id, data)
rw.RUnlock()
}
func write(id int, wg *sync.WaitGroup) {
defer wg.Done()
rw.Lock()
data = id
fmt.Printf("Writer %d writing data: %d\n", id, data)
rw.Unlock()
}
func main() {
var wg sync.WaitGroup
// 启动多个读操作
for i := 1; i <= 3; i++ {
wg.Add(1)
go read(i, &wg)
}
// 启动多个写操作
for i := 1; i <= 2; i++ {
wg.Add(1)
go write(i, &wg)
}
time.Sleep(2 * time.Second)
wg.Wait()
}
在这个示例中,通过RWMutex
的读锁和写锁,在读操作时允许多个goroutine并发读取,而在写操作时保证独占,从而在读写频繁的场景下平衡性能,既保证数据一致性又尽量提高并发效率。