std::counting_semaphore的初始值即最大资源数,构造后不可动态调整,必须重建对象才能变更容量;其计数可累积,而binary_semaphore仅限0/1状态且release超限时行为未定义。
std::counting_semaphore 的初始值就是最大资源数
它不提供运行时动态调整容量的接口,initial_count 在构造时就锁死了能同时通行的线程上限。比如 std::counting_semaphore{2} 表示最多 2 个线程能同时持有许可;设成 {0} 就是纯阻塞信号量,等同于二元信号量的“初始不可用”状态。
常见错误是误以为能像某些第三方库那样调用 set_capacity() 或类似方法扩容——C++20 标准里没有这个能力。资源数变更必须靠重建对象(注意:这会丢失当前所有等待线程的状态)。
- 初始化值必须是非负整数,负值触发编译期断言失败(
static_assert) - 模板参数
N是底层计数类型的最大安全位宽,不是资源数;实际资源数由构造参数决定 - 若资源数很大(如百万级),
std::counting_semaphore可能比更省内存,但要注意溢出风险
acquire() 和 release() 的语义与典型误用
acquire() 是阻塞式申请一个许可,release() 是归还一个许可。两者都保证原子性,但 release() 永远不会阻塞(即使当前计数已达最大值,标准允许它静默忽略或抛异常,实践中主流实现(libstdc++、libc++)选择忽略)。
最容易踩的坑是:在未成功 acquire() 前就调用 release(),或者重复 release() 导致计数超过初始值——这不违法,但会破坏你对“资源数”的建模逻辑。例如你用信号量保护 3 个数据库连接,却意外 release() 了 5 次,后续 acquire() 就可能拿到“虚高”的许可。
立即学习“C++免费学习笔记(深入)”;