Golang sync 包底层原理
目录
Go 语言的 sync package 是并发编程的核心。面试和实际开发中的考点通常集中在底层原理、正常/饥饿模式切换、CAS 原子操作以及内存屏障。
以下是 sync 包中 5 个最核心的考点及其 Mermaid 原理图
1. sync.Mutex:正常模式与饥饿模式
考点: 为了防止 Goroutine 被“饿死”,Mutex 会在“正常模式”和“饥饿模式”之间切换。
- 正常模式: 性能好,新来的 Goroutine 会竞争锁(自旋),可能导致等待队列里的 Goroutine 抢不到锁。
- 饥饿模式: 公平,锁会直接交给等待队列头部的 Goroutine,新来的不自旋,直接排队。
2. sync.Once:双重检测锁 (DCL)
考点: 保证全局只执行一次。核心是 done 标志位(原子操作)和 Mutex(处理并发请求)。
- 误区: 不能只用原子操作,因为如果两个协程同时进来,其中一个执行时,另一个必须阻塞等待,否则会拿到一个初始化一半的对象。
3. sync.WaitGroup:计数器与信号量
考点: 如何实现“等待所有任务完成”。底层是一个 64 位的复合变量(Counter + Waiter)和一个信号量。
- 原理:
Add修改计数器,Wait检查计数器是否为 0,不为 0 则进入信号量阻塞。
4. sync.Map:读写分离与空间换时间
考点: 为什么在高并发读时性能好?因为它用了两个 Map:read(原子只读,不加锁)和 dirty(加锁写)。
- Miss 升级: 当
read频繁未命中(Miss)时,会将dirty提升为read。
5. sync.Pool:GC 影响与 Per-P 缓存
考点: 用于对象复用,减轻 GC 压力。
- 核心: 每一个调度器 P 都有自己的
localPool,优先访问本地,避免锁竞争。 - 注意:
sync.Pool里的对象会在 GC 时被清理,不能当作长期缓存(如 Redis)使用。
总结:
- Mutex: 关注自旋与饥饿模式。
- Once: 关注双重检查锁。
- WaitGroup: 关注原子计数器与信号量唤醒。
- Map: 关注读写分离与Dirty 提升机制。
- Pool: 关注Per-P 缓存与GC 清理逻辑。