



























sync.Map 是 Go 语言标准库 sync 包中提供的一种 并发安全的 map 类型。
sync.Map? Go 原生的 map 不是并发安全的。如果多个 goroutine 同时对一个普通 map 进行读写(尤其是写),会导致 panic(data race):
go
// 危险!多个 goroutine 并发写 map 会 panic
var m = make(map[string]int)
go func() { m["a"] = 1 }()
go func() { m["b"] = 2 }()为了解决这个问题,通常有几种方式:
sync.Mutex 或 sync.RWMutex 手动加锁;sync.Map —— Go 官方提供的并发安全 map。sync.Map 的特点 | 特性 | 说明 |
|---|---|
| ✅ 并发安全 | 多个 goroutine 可以同时读写,无需额外加锁 |
⚠️ 不支持 len()、range 等语法 | 必须使用其方法操作 |
| ⚠️ 无泛型(Go 1.18 之前) | 键和值都是 interface{} 类型(Go 1.21+ 虽然支持泛型,但 sync.Map 本身仍未泛型化) |
| 🔧 适合特定场景 | 官方文档建议:仅在以下情况使用: • 读远多于写 • 多个 goroutine 访问不同的 key(即 key 集合基本不变或增长缓慢) |
📌 注意:不要盲目用
sync.Map替代所有 map!对于大多数普通场景,map + RWMutex性能更好、类型更安全。
go
package main
import (
"fmt"
"sync"
)
func main() {
var sm sync.Map
// 写入
sm.Store("name", "Alice")
sm.Store("age", 30)
// 读取
if val, ok := sm.Load("name"); ok {
fmt.Println("name:", val) // name: Alice
}
// 读取或写入(不存在则写入)
actual, loaded := sm.LoadOrStore("city", "Beijing")
if loaded {
fmt.Println("已存在:", actual)
} else {
fmt.Println("新写入:", actual) // 新写入: Beijing
}
// 删除
sm.Delete("age")
// 遍历
sm.Range(func(key, value interface{}) bool {
fmt.Println(key, "=", value)
return true // 返回 false 会中断遍历
})
}| 方法 | 说明 |
|---|---|
Store(key, value interface{}) | 存储键值对 |
Load(key interface{}) (value interface{}, ok bool) | 读取,ok 表示是否存在 |
LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) | 如果 key 不存在则存入并返回新值;否则返回已有值 |
LoadAndDelete(key interface{}) (value interface{}, loaded bool) | 读取并删除 |
Delete(key interface{}) | 删除键 |
Range(f func(key, value interface{}) bool) | 遍历所有键值对 |
Range 统计)。int,取出是 interface{},需类型断言:go
if v, ok := sm.Load("age"); ok {
age := v.(int) // 注意类型安全!
}
sync.Map是 Go 提供的一个 开箱即用的并发安全 map,适用于 读多写少、key 分散 的场景。
但它不是万能的,不要把它当作普通 map 的并发替代品,应根据实际场景选择合适方案。
SyncMap[K, V] 下面是一个安全、易用、类型安全的泛型封装,底层使用 sync.Map:
go
package sync_map_util
import (
"sync"
)
// SyncMap 是 sync.Map 的泛型封装
type SyncMap[K comparable, V any] struct {
m sync.Map
}
// Store 设置键值对
func (sm *SyncMap[K, V]) Store(key K, value V) {
sm.m.Store(key, value)
}
// Load 获取值,第二个返回值表示 key 是否存在
func (sm *SyncMap[K, V]) Load(key K) (value V, ok bool) {
v, ok := sm.m.Load(key)
if !ok {
var zero V
return zero, false
}
return v.(V), true
}
// LoadOrStore 如果 key 不存在则存储并返回新值;否则返回已有值
func (sm *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
v, loaded := sm.m.LoadOrStore(key, value)
return v.(V), loaded
}
// LoadAndDelete 加载并删除
func (sm *SyncMap[K, V]) LoadAndDelete(key K) (value V, loaded bool) {
v, loaded := sm.m.LoadAndDelete(key)
if !loaded {
var zero V
return zero, false
}
return v.(V), true
}
// Delete 删除键
func (sm *SyncMap[K, V]) Delete(key K) {
sm.m.Delete(key)
}
// Range 遍历所有元素,f 返回 false 时停止遍历
func (sm *SyncMap[K, V]) Range(f func(key K, value V) bool) {
sm.m.Range(func(key, value interface{}) bool {
return f(key.(K), value.(V))
})
}
// Len 获取元素个数(注意:非原子操作,仅用于调试或低并发场景)
func (sm *SyncMap[K, V]) Len() int {
count := 0
sm.Range(func(key K, value V) bool {
count++
return true
})
return count
}go
package main
import "fmt"
func main() {
// 创建一个 string -> int 的并发安全 map
var m SyncMap[string, int]
m.Store("apple", 10)
m.Store("banana", 20)
if val, ok := m.Load("apple"); ok {
fmt.Println("apple =", val) // apple = 10
}
m.Range(func(k string, v int) bool {
fmt.Printf("%s: %d\n", k, v)
return true // 继续遍历
})
fmt.Println("Total items:", m.Len()) // Total items: 2
}Len() 不是原子操作,也不高效(需要遍历),不要在高并发或性能敏感路径中使用。sync.Map 的所有语义和适用场景(读多写少、key 分散等)。此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。