Go原生map非并发安全,同时读写会panic;sync.Map专为读多写少设计,但不支持range、len,遍历需用Range方法,高频写入性能反不如加锁的普通map。

为什么不能直接用普通 map 做并发读写
Go 的原生 map 不是并发安全的——只要同时有 goroutine 在写,或者一写多读但没加锁,运行时大概率 panic:fatal error: concurrent map writes 或 concurrent map read and map write。这不是概率问题,是明确禁止的行为。别指望“我只读不写”就安全,因为底层哈希扩容时会触发写操作。
sync.Map 的适用场景和代价
sync.Map 是为「读多写少」场景优化的并发安全 map,内部用了读写分离 + 延迟初始化 + 普通 map + 只读快照等策略。但它不是万能替代品:
- 不支持遍历(
range)——必须用Range()方法传回调函数 - 没有
len(),无法直接获取元素数量 - 删除后 key 不会立即从内存释放(可能滞留在 dirty map 中)
- 高频写入性能反而比加
sync.RWMutex的普通 map 差
sync.Map 基本用法与常见错误
它提供的是方法调用接口,不是语法糖,所有操作都通过方法完成,且 key/value 类型都是 interface{},需注意类型断言风险。
var m sync.Map
// ✅ 正确:存、取、删、判断存在
m.Store("user_id_123", &User{Name: "Alice"})
if val, ok := m.Load("user_id_123"); ok {
user := val.(*User) // 注意类型断言,失败会 panic
}
m.Delete("user_id_123")
_, exists := m.Load("user_id_123") // exists == false
// ❌ 错误:不能 range,不能用 [] 语法,不能用 len()
// for k, v := range m {} // 编译错误
// m["key"] = value // 编译错误
// len(m) // 编译错误
遍历 sync.Map 的唯一安全方式
必须用 Range(),它接收一个函数参数,该函数会在每个键值对上被同步调用。注意:函数内不能修改 sync.Map(否则行为未定义),也不能依赖遍历顺序(无序)。
立即学习“go语言免费学习笔记(深入)”;
m.Range(func(key, value interface{}) bool {
k := key.(string)
u := value.(*User)
fmt.Printf("Key: %s, User: %s\n", k, u.Name)
return true // 继续遍历;返回 false 则中止
})
如果需要先收集全部 key 再处理,得自己建 slice 存下来——Range() 是唯一入口,没有「获取所有 key」的方法。
sync.RWMutex + map 更可控。它不是并发 map 的银弹,而是特定负载下的折中解。










