sync 包
当多个 goroutine 需要访问共享状态时,必须使用同步原语避免数据竞争。sync 包提供了标准工具集。
竞争问题的演示
使用 go run -race main.go 运行上述代码,竞态检测器会明确报告竞争位置。
sync.Mutex:互斥锁
Tip
锁的粒度应尽量小:只在临界区内访问共享数据,I/O、网络调用与耗时计算放在锁外。持锁期间调用未知代码(回调等)容易引发死锁。
sync.RWMutex:读写锁
读多写少的场景使用读写锁,允许多个读操作并行:
sync.Once:单次执行
保证某段逻辑在并发环境下只执行一次,典型场景是单例初始化:
sync.Map:并发安全的映射
sync.Map 针对读多写少、键集合稳定的场景做了专门优化,且零值即可使用:
Tip
sync.Map 的类型为 map[any]any,缺乏编译期类型检查,读写性能在写多场景下不如 Mutex + map。一般业务代码优先使用 Mutex + map[K]V(可配合泛型封装);仅在缓存、读密集等特定负载下使用 sync.Map。
原子操作:sync/atomic
对单一数值的增减与交换,原子操作比加锁开销更低:
Go 1.19 起提供泛型形式 atomic.Int64、atomic.Pointer[T] 等类型,进一步简化使用:
小结
- 共享状态必须保护:
Mutex通用,RWMutex优化读多写少。 sync.Once实现并发安全的单次初始化。sync.Map与sync/atomic适用于特定负载;通用场景优先Mutex + map。