c++oding="utf-8" ?>
std::binary_semaphore 不是标准 c++20/23 的组成部分,实际应使用 std::counting_semaphore{1} 作为二元信号量;它语义等价、无锁、低延迟、支持跨线程释放,但需注意初始化、内存序、公平性等细节。

std::binary_semaphore 在标准 C++20 / C++23 中根本不可用 —— 它不是标准库的一部分,也没有被任何正式版本接纳。你看到的 std::binary_semaphore 要么是编译器实验性扩展(如 MSVC 的 std::experimental::binary_semaphore),要么是误把 std::counting_semaphore 当成了 binary 版本。
真正可用、轻量、无锁、低延迟的二元同步原语,是 std::counting_semaphore。
为什么 std::counting_semaphore 就是你需要的 binary 信号量
它语义上完全等价:计数器只能是 0 或 1;acquire() 阻塞直到值为 1,然后原子减为 0;release() 原子加为 1 并唤醒等待者。
- 底层在 x86-64 + GCC/Clang + libstdc++/libc++ 上是纯用户态原子操作(如
lock xadd),无 futex 回退(无争用时延迟在纳秒级) - 不依赖
std::mutex,没有所有权概念,任意线程可release(),哪怕不是acquire()的那个线程 - 构造时必须传入编译期常量
1:std::counting_semaphore sem{0};,不能写成std::counting_semaphore再只用 0/1 —— 否则最大值冗余,可能影响优化或触发非最优路径
std::counting_semaphore 的典型误用与修复
常见错误现象:acquire() 永远阻塞、线程卡死、性能比 std::mutex 还差。
- 初始化值错设为
0但忘了配对release():比如生产者还没发信号,消费者就调了acquire()—— 这不是 bug,是设计意图;但若漏掉某次release(),就会永久挂起 - 在 hot path 上反复
release()后立刻acquire():这会强制执行原子读-改-写(RMW),即使没竞争也引入额外开销;应改为用try_acquire()+yield()轮询(仅限极短等待) - 跨线程释放时未保证内存序:虽然
acquire()/release()默认使用memory_order_acquire/memory_order_release,但若需和非原子变量同步(如通知某个 flag 变了),要显式搭配std::atomic_thread_fence或用带 memory_order 参数的try_acquire_for
替代 std::mutex + std::condition_variable 的真实收益点
用 std::counting_semaphore 替换传统条件变量组合,只在特定场景有明显优势:
- 一次性通知(one-shot signal):比如主线程等 worker 初始化完成,只需
sem.release()一次,worker 线程sem.acquire()即可 —— 没有虚假唤醒,无需 while 循环检查 predicate - 高频率、低延迟的“门控”同步:例如高频数据采集线程等某个硬件就绪信号,
acquire()延迟比std::condition_variable::wait()低一个数量级(微秒 → 纳秒) - 避免锁嵌套:当已有 mutex 被持有时,再调
cv.wait()会延长临界区;而sem.acquire()是独立原语,不关联任何锁
最容易被忽略的细节:公平性与平台差异
std::counting_semaphore 的唤醒顺序在标准中未规定,默认不保证 FIFO。在 Linux 上,glibc 实现通常基于 futex_wait,实际行为接近公平;但在某些旧内核或 musl 环境下,可能偏向“唤醒最近阻塞的线程”,导致某个线程长期饥饿。
- 若业务逻辑严格要求顺序(如 ABC 打印),必须靠信号量链路设计保障(
semA→semB→semC→semA),而非依赖单个信号量的唤醒公平性 - Clang/GCC 支持
-fsemaphore-fair编译选项启用公平调度,但会略微增加开销;生产环境开启前务必压测 - Windows 上对应的是
WaitOnAddress,同样不承诺绝对公平,且try_acquire_until的超时精度受系统时钟粒度限制(通常 ≥15ms)
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











