sync.Map 是內建的併發安全字典,針對特定使用模式做了最佳化。
基本操作#
var m sync.Map
// 儲存
m.Store("key", "value")
// 載入
value, ok := m.Load("key")
if ok {
fmt.Println(value.(string))
}
// 載入或儲存(若不存在則儲存)
actual, loaded := m.LoadOrStore("key2", "default")
// 刪除
m.Delete("key")
// 遍歷
m.Range(func(key, value interface{}) bool {
fmt.Printf("%v: %v\n", key, value)
return true // 回 false 可中斷遍歷
})鍵型別限制#
sync.Map的鍵型別不能是:
- 函式型別
- 字典型別
- 切片型別
違反此規則會在執行時期 panic。
型別安全封裝#
sync.Map 用 interface{} 作為鍵值型別,建議外層封裝出型別安全的 API:
type IntStringMap struct {
m sync.Map
}
func (m *IntStringMap) Store(key int, value string) {
m.m.Store(key, value)
}
func (m *IntStringMap) Load(key int) (string, bool) {
v, ok := m.m.Load(key)
if !ok {
return "", false
}
return v.(string), true
}
func (m *IntStringMap) Delete(key int) {
m.m.Delete(key)
}sync.Map vs map + Mutex#
| 特性 | sync.Map | map + Mutex |
|---|---|---|
| 適用場景 | 讀多寫少、鍵穩定 | 通用場景 |
| 效能 | 讀取最佳化、減少鎖競爭 | 穩定但有鎖開銷 |
| 型別安全 | 需要型別斷言 | 編譯時期檢查 |
| 記憶體 | 較高(維護兩份資料) | 較低 |
何時使用 sync.Map:
- 鍵值對只增不減或很少刪除
- 讀操作遠多於寫操作
- 不同 goroutine 操作不同的鍵
其他情況直接用
map + sync.RWMutex即可,反而更清晰、效能也不會差。