核心语法 #
var (
validationCache = &sync.Map{}
cacheStats = struct {
sync.RWMutex
hits int64
misses int64
}{}
)
语法拆解 #
sync.Map{}
#
- 字面量初始化:创建sync.Map零值实例
- 零值可用:Go中sync.Map的零值状态即可直接使用
& 取址符
#
- 功能:获取对象内存地址
- 转换:值类型 → 指针类型
- 必要性:sync.Map必须使用指针共享状态
不同初始化方式对比 #
// 1. 指针初始化 (推荐)
cache := &sync.Map{}
// 2. new() 函数
cache := new(sync.Map) // 等价于 &sync.Map{}
// 3. 分步声明
var cache *sync.Map
cache = &sync.Map{}
// 4. 值类型 (错误用法)
var cache sync.Map // 拷贝时数据丢失
为什么必须用指针? #
sync.Map内部结构 #
type Map struct {
mu Mutex // 互斥锁
read atomic.Value // 原子值
dirty map[interface{}]*entry
misses int
}
关键原因 #
- 结构体较大:包含锁、原子值、map等复杂字段
- 状态共享:多协程需要访问同一份数据
- 地址稳定:内部锁机制要求固定内存位置
值拷贝 vs 指针共享 #
// 错误:值拷贝导致数据丢失
func badExample() {
var cache sync.Map // 值类型
cache.Store("key", "value")
processCache(cache) // 传递时完整拷贝结构体
// 在processCache中的修改不会影响原cache
}
// 正确:指针共享同一实例
func goodExample() {
cache := &sync.Map{} // 指针类型
cache.Store("key", "value")
processCache(cache) // 传递指针,共享数据
// 所有函数操作同一份数据
}
内存布局对比 #
值类型传递:
函数A: [sync.Map实例A]
函数B: [sync.Map实例B] ← 完全独立的副本
指针传递:
函数A: [指针] ──┐
函数B: [指针] ──┼──→ [堆中唯一的sync.Map实例]
函数C: [指针] ──┘
匿名结构体嵌入 #
cacheStats := struct {
sync.RWMutex // 嵌入读写锁
hits int64
misses int64
}{}
// 使用方式
cacheStats.Lock() // 获得写锁
cacheStats.hits++
cacheStats.Unlock()
cacheStats.RLock() // 获得读锁
hits := cacheStats.hits
cacheStats.RUnlock()
最佳实践 #
✅ 推荐用法 #
// 全局变量
var globalCache = &sync.Map{}
// 结构体字段
type Service struct {
cache *sync.Map
}
func NewService() *Service {
return &Service{
cache: &sync.Map{},
}
}
❌ 避免用法 #
// 值类型字段
type BadService struct {
cache sync.Map // 结构体拷贝时数据丢失
}
// 局部值变量
func process() {
var cache sync.Map // 无法在函数间共享
}
性能影响 #
| 操作 | 值类型 | 指针类型 |
|---|---|---|
| 赋值 | 拷贝整个结构体 | 拷贝8字节指针 |
| 传参 | 栈上分配大对象 | 传递小指针 |
| 并发 | 每份拷贝独立 | 共享同一实例 |