一、读写锁的概念
在并发编程中,读写锁是一种常见的同步机制。读写锁分为两种锁:读锁和写锁。多个 goroutine 可以同时获取读锁,但只能有一个 goroutine 获取写锁,其他 goroutine 等待写锁的释放。
读写锁的实现可以使用 sync
包中的 RWMutex
类型。使用 RWMutex
时,通过调用 RLock
和 RUnlock
方法来获取和释放读锁,调用 Lock
和 Unlock
方法来获取和释放写锁。
var rwMutex sync.RWMutex
// 获取读锁
rwMutex.RLock()
// 使用共享资源
// 释放读锁
rwMutex.RUnlock()
// 获取写锁
rwMutex.Lock()
// 使用独占资源
// 释放写锁
rwMutex.Unlock()
二、使用读写锁的场景
通常情况下,读操作的频率远高于写操作。在这种情况下,使用读写锁可以提高并发访问共享资源的效率。
三、写操作实例
下面的代码演示了如何使用 RWMutex
实现线程安全的计数器,计数器可以使用 Get
方法获取当前计数值,使用 Add
方法增加计数值。
type Counter struct {
value int
rwMutex sync.RWMutex
}
func (c *Counter) Get() int {
c.rwMutex.RLock()
defer c.rwMutex.RUnlock()
return c.value
}
func (c *Counter) Add(n int) {
c.rwMutex.Lock()
defer c.rwMutex.Unlock()
c.value += n
}
四、读操作实例
下面的代码演示了如何使用 RWMutex
实现线程安全的缓存,缓存可以使用 Get
方法获取缓存值,使用 Set
方法设置缓存值。
type Cache struct {
cache map[string]string
rwMutex sync.RWMutex
}
func (c *Cache) Get(key string) string {
c.rwMutex.RLock()
defer c.rwMutex.RUnlock()
return c.cache[key]
}
func (c *Cache) Set(key string, value string) {
c.rwMutex.Lock()
defer c.rwMutex.Unlock()
c.cache[key] = value
}
五、读写锁的注意事项
在使用读写锁时,需要注意以下几点:
- 当需要更新共享资源时,必须持有写锁才能更新;
- 当只需要读取共享资源时,应该持有读锁,以允许多个 goroutine 在同一时间读取资源;
- 在使用
RLock
和RUnlock
获取和释放读锁时,必须保证其成对出现,否则可能会导致死锁; - 在使用
Lock
和Unlock
获取和释放写锁时,必须保证其成对出现,否则可能会导致程序出现异常。