Skip to main content

Go: 标准库 sync

📅 2026-03-13 ✏️ 2026-03-16 Go 标准库 CS GO

1 · 标准库 sync#

提供同步原语:锁、等待组、单例、对象池、条件变量、支持并发的map

同步原语就是多个执行单元(goroutine)之间协调行为的基本工具

go doc sync
协调方式类型用途描述
互斥Mutex / RWMutex互斥锁 / 读写锁同一时刻只有一个能访问;RW 优化读多写少,读共享写互斥
等全部完成WaitGroup等待一组 goroutine 完成Add 设总数,Done 减一,Wait 阻塞直到归零
只执行一次Once / OnceFunc / OnceValue确保函数只执行一次多个 goroutine 同时调用,函数体只执行一次(懒初始化)
信号通知Cond条件变量,用于 goroutine 间信号通知#Cond
并发安全访问Map并发安全的 map(适合读多写少或 key 不相交场景)内置并发安全,无需额外加锁
复用对象Pool临时对象池,减少 GC 压力多 goroutine 共享对象池,Get/Put 并发安全,非传统同步原语

1.1 · Cond#

围绕共享状态做等待/通知协调的机制

  1. Cond 本身并不保存这个条件,需要自定义条件:通常是通知方修改条件,等待方检查条件;
  2. Cond 必须配合一把锁一起用,通常是 sync.Mutex
  3. 等待方调用Wait()时必须已经持有锁
  4. Wait()会”原子地”先解锁再睡眠(阻塞),被唤醒后会重新加锁(需要竞争锁)再返回
  5. 一定要写成 for !condition { cond.Wait() },不要写成 if:被唤醒时,条件不一定还成立

一般流程是:

  1. 等待方先加锁,检查条件
  2. 条件不满足,Wait() 挂起
  3. 通知方加锁,修改条件
  4. 通知方调用 Signal/Broadcast
  5. 等待方被唤醒,重新竞争拿锁,再次检查条件

重要点:

  • 条件需要自定义:通常是通知方修改条件,但是等待方也可是改(表示消费掉了这个条件)
  • 等待方被唤醒后,还是需要竞争锁:也就是说Broadcast唤醒全部后,也是排队竞争锁

NOTE: Cond 将数据、通知拆分了,不是一个原子整体,增加心智负担;chan的数据、绑定一起了。

2 · sync vs. chan#

chan 也是用于协调goroutine的工具,但是与sync有点不同。chan 以通信的方式,而sync使用共享内容的方式。

ksync 原语chan
思路通过共享内存通信通过通信共享内存
关注点保护数据,控制访问权传递数据,协调流程
典型场景计数器、缓存、对象池流水线、扇入扇出、信号通知
粒度更底层,性能更好更高层,表达力更强

x/sync#

协调方式类型用途
等全部完成(带错误)errgroup#errgroup
合并重复singleflight#singleflight
限制并发数semaphore#semaphore

3.1 · errgroup#

带错误处理和取消能力的 WaitGroup,任一出错可取消全部

3.2 · singleflight#

合并重复调用,同 key 只执行一次

相同key,只调用最开始的,后续的都不执行,只等待

3.3 · semaphore#

同时最多允许多少个任务占用资源

权重:每个任务占用的资源权重可以不一样