缓存模块之并发编程-sync.Muxtex入门和double-check
第九周_缓存模块之并发编程_sync.Mutex入门和double-check
sync包——Mutex和RWMutex
Mutex可以看做是锁,而RWMutex则是读写锁。一般的用法是将 Mutex 或者 RWMutex 和需要被保住的资源封装在一个结构体内。
- 如果有多个 goroutine 同时读写的资源,就一定要保护起来。
- 如果多个 goroutine 只读某个资源,那就不需要保护。
使用锁的时候,优先使用 RWMutex。
- RWMutex:核心就是四个方法,RLock、RUnlock、Lock、Unlock
- Mutex:Lock 和 Unlock
举例
SafeMap 的 LoadOrStore 写法
例子:SafeMap 可以看做是 map 的一个线程安全的封装。我们为它增加一个 LoadOrStore 的方法。
// safe_map.go
package sync
import "sync"
type SafeMap[K comparable, V any] struct {
data map[K]V
mutex sync.RWMutex
}
func (s *SafeMap[K, V]) Put(key K, val V) {
s.mutex.Lock()
defer s.mutex.Unlock() // 为什么在这调用defer解锁,是因为下一行代码如果发生panic,函数退出会调用这个defer进行解锁;
s.data[key] = val
//s.mutex.Lock()
//s.data[key] = val
// 发生panic;直接退出,不会释放锁;故多数建议使用defer s.mutex.Unlock()进行解锁;
//s.mutex.Unlock()
}
func (s *SafeMap[K, V]) Get(key K) (any, bool) {
s.mutex.RLock()
defer s.mutex.RUnlock()
res, ok := s.data[key]
return res, ok
}
func (s *SafeMap[K, V]) LoadOrStore(key K, newVal V) (val V, loaded bool) {
s.mutex.RLock()
res, ok := s.data[key]
s.mutex.RUnlock()
if ok { // 存在则返回
return res, true
}
// 不存在则新建
s.mutex.Lock()
defer s.mutex.Unlock()
// double check 写法
res, ok = s.data[key]
if ok { // 存在则返回
return res, true
}
s.data[key] = newVal
return newVal, false
}
// safe_map_test.go
package sync
import (
"testing"
"time"
)
func TestSafeMap_LoadOrStore(t *testing.T) {
s := &SafeMap[string, string]{
data: make(map[string]string),
}
/**
没有加double check,输出:
=== RUN TestSafeMap_LoadOrStore
safe_map_test.go:15: 协程1 value: value1 false
safe_map_test.go:20: 协程2 value: value2 false
--- PASS: TestSafeMap_LoadOrStore (1.41s)
加double check,输出:
=== RUN TestSafeMap_LoadOrStore
safe_map_test.go:27: 协程2 value: value1 true
safe_map_test.go:22: 协程1 value: value1 false
--- PASS: TestSafeMap_LoadOrStore (1.01s)
*/
go func() {
val, ok := s.LoadOrStore("key1", "value1")
t.Log("协程1 value: ", val, ok)
}()
go func() {
val, ok := s.LoadOrStore("key1", "value2")
t.Log("协程2 value: ", val, ok)
}()
time.Sleep(time.Second)
}
double-check 写法
使用 RWMutex 实现 double-check:
- 加读锁先检查一遍
- 释放读锁
- 加写锁
- 再检查一遍