在 Go 中,读写锁(`sync.RWMutex`)是一种特殊的锁,允许多个读操作并发执行,但写操作是独占的。读写锁适用于读多写少的场景,能够显著提高并发性能。
### `sync.RWMutex` 的核心方法
– **`RLock()`:** 获取读锁。如果写锁未被持有,多个协程可以同时获取读锁;如果写锁被持有,读锁会阻塞。
– **`RUnlock()`:** 释放读锁。
– **`Lock()`:** 获取写锁。如果读锁或写锁已被持有,写锁会阻塞,直到所有锁被释放。
– **`Unlock()`:** 释放写锁。
### 读写锁的特点
1. **读共享:** 多个协程可以同时持有读锁。
2. **写独占:** 写锁是独占的,同一时间只能有一个协程持有写锁。
3. **读写互斥:** 写锁会阻塞所有读锁和写锁,读锁会阻塞写锁。
### 使用场景
– 适用于读操作远多于写操作的场景,如缓存系统、配置管理等。
### 示例代码
#### 读写锁的基本使用
package main import ( "fmt" "sync" "time" ) type SafeMap struct { data map[string]string rw sync.RWMutex } func NewSafeMap() *SafeMap { return &SafeMap{ data: make(map[string]string), } } func (m *SafeMap) Get(key string) string { m.rw.RLock() // 获取读锁 defer m.rw.RUnlock() // 释放读锁 return m.data[key] } func (m *SafeMap) Set(key, value string) { m.rw.Lock() // 获取写锁 defer m.rw.Unlock() // 释放写锁 m.data[key] = value } func main() { sm := NewSafeMap() // 启动多个读协程 for i := 0; i < 5; i++ { go func(i int) { for { value := sm.Get("key") fmt.Printf("Reader %d: %s\n", i, value) time.Sleep(100 * time.Millisecond) } }(i) } // 启动一个写协程 go func() { for i := 0; i < 10; i++ { sm.Set("key", fmt.Sprintf("value%d", i)) fmt.Printf("Writer: value%d\n", i) time.Sleep(200 * time.Millisecond) } }() time.Sleep(3 * time.Second) // 等待协程执行 }
#### 输出示例
“`
Reader 0: value0
Reader 1: value0
Reader 2: value0
Reader 3: value0
Reader 4: value0
Writer: value1
Reader 0: value1
Reader 1: value1
Reader 2: value1
Reader 3: value1
Reader 4: value1
Writer: value2
…
“`
### 注意事项
1. **避免死锁:** 确保在获取锁后释放锁,尤其是在使用 `defer` 时。
2. **写锁优先级:** Go 的 `sync.RWMutex` 是写优先的,写锁会优先于读锁获取。
3. **性能开销:** 虽然读写锁提高了读操作的并发性,但仍有一定的性能开销,需根据场景选择合适的锁。
### 总结
– **`sync.RWMutex`** 适用于读多写少的场景,能够显著提升并发性能。
– 通过 **`RLock()`** 和 **`RUnlock()`** 实现并发读,通过 **`Lock()`** 和 **`Unlock()`** 实现独占写。
– 使用时需注意避免死锁和性能开销。