Go标准库无内置信号量,需用带缓冲channel(如make(chan struct{}, n))模拟,其容量即最大并发数;误用无缓冲channel或chan int、或仅用Mutex加计数器均会导致逻辑错误。

Go 里没有内置的 semaphore 类型,得用 sync.Mutex + sync.Cond 或 channel 模拟
Go 标准库确实不提供 semaphore(信号量),sync 包里只有 WaitGroup、Mutex、RWMutex、Cond 等基础原语。想实现经典信号量(支持 acquire/release、可计数、可阻塞),必须自己封装。最常用且轻量的方式是用带缓冲的 channel:make(chan struct{}, n) —— 它天然具备“最多 n 个并发”的语义。
常见错误是误用无缓冲 channel 或把 chan int 当信号量:前者只能串行,后者无法限制数量;还有人直接用 sync.Mutex 加计数器,但忘了处理等待逻辑,导致忙等或死锁。
- 用
chan struct{}实现最简信号量:发送一个struct{}占位,接收一个释放位置 - 初始化容量即为最大并发数,比如
make(chan struct{}, 5)表示最多 5 个 goroutine 同时进入 - 调用
sem 是 acquire,<code><-sem是 release;注意顺序,别在 defer 里写反 - channel 方式天然支持 select + timeout,比手写
Cond更简洁、更符合 Go 的并发哲学
为什么不用 sync.Cond 手写信号量?
理论上可以:用 sync.Mutex 保护一个计数器,配合 sync.Cond 唤醒等待者。但实际开发中极少这么干,因为容易出错:
-
Cond.Wait必须在Mutex持有状态下调用,且唤醒后需重新检查条件 —— 多一重循环和锁管理,代码易错 - 需要手动维护等待队列或靠 goroutine 自旋,不如 channel 的调度由 runtime 统一管理可靠
- Go 的 channel 已经是经过深度优化的同步原语,性能不输手写
Cond,还自带公平性与 GC 友好特性 - 标准库中的
errgroup.Group和第三方库如golang.org/x/sync/semaphore全部基于 channel 实现
用 golang.org/x/sync/semaphore 是最稳妥的选择
如果你不想重复造轮子,官方维护的扩展包 golang.org/x/sync/semaphore 就是为此而生。它提供 Weighted 类型,支持带权重的 acquire(比如某些任务消耗 2 单位资源,另一些只占 1),比简单计数更灵活。
立即学习“go语言免费学习笔记(深入)”;
在 Golang 中使用 samber/hot 进行内存缓存,支持 LRU、LFU、TinyLFU、W‑TinyLFU、S3FIFO、ARC、TwoQueue、SIEVE、FIFO 等淘汰算法,提供 TTL、缓存加载器及分片功能。
安装:go get golang.org/x/sync/semaphore
基本用法:
sem := semaphore.NewWeighted(3) // 最多 3 单位并发
err := sem.Acquire(ctx, 1) // 阻塞直到拿到 1 单位
if err != nil {
return err
}
defer sem.Release(1) // 必须配对,否则资源泄漏
-
Acquire支持context.Context,可取消或超时,这是手写 channel 不易直接支持的 -
Release不校验是否已 acquire,务必确保成对调用,否则计数错乱会导致后续永久阻塞 - 如果传入负权重或释放超过已获取量,会 panic —— 运行时检查比静默失败更安全
- 内部仍是基于 channel 实现,但封装了上下文感知、公平排队、错误传播等细节
channel 实现的信号量要注意阻塞不可取消
纯 chan struct{} 方案最大的短板是:一旦 sem 阻塞,就无法被 context 中断。这在需要响应 cancel 或 deadline 的场景下很危险(比如 HTTP handler 中限流)。
解决办法只有两个:
- 改用
golang.org/x/sync/semaphore,它所有 acquire 都接受ctx - 自己封装 channel + select,例如:
select { case sem ,但要小心重复 acquire 或漏 release
真正上线的服务,尤其是涉及用户请求或外部依赖的,别省那几行代码 —— 直接上 x/sync/semaphore。它的实现已经过 Kubernetes、etcd 等项目长期验证,边界情况处理得比你自己写的更稳。

















