跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

C++中std::counting_semaphore怎么控制资源数_C++20信号量用法【并发】

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()
永远不会阻塞(即使当前计数已达最大值,标准允许它静默忽略或抛异常,实践中主流实现(libstd c++ 、libc++)选择忽略)。 最容易踩的坑是:在未成功
acquire()
前就调用
release()
,或者重复
release()
导致计数超过初始值——这不违法,但会破坏你对“资源数”的建模逻辑。例如你用信号量保护 3 个数据库连接,却意外
release()
了 5 次,后续
acquire()
就可能拿到“虚高”的许可。 立即学习 “ C++免费学习笔记(深入) ”; Trae win Trae Windows 官方版本现已全面支持 Win10 和 Win11 系统(64位)。国内开发者可直接访问 Trae 中文官网(trae.cn 或 trae.com.cn),一键下载官方正版 .exe 安装包,享受国内直连的高速稳定体验。安装过程极其简便,双击即可运行,并支持一键无缝导入 VS Code 或 Cursor 的原有主题、快捷键及插件配置。登录支持手机号、微信或稀土掘金账号。内置免费的豆包与 DeepSeek 等顶级大模型,让你开箱即用,轻松开启高效的 AI 智能编程之旅。 下载 务必配对使用:每个逻辑上的“进入临界区”对应一次
acquire()
,每个“退出”对应一次
release()
推荐用 RAII 封装,比如自定义
semaphore_guard
,避免异常绕过
release()
try_acquire()
可做非阻塞探测,返回
bool
,适合超时或轮询场景 和 std::binary_semaphore 的关键区别在哪
std::binary_semaphore
是
std::counting_semaphore
的别名,但语义更严格:它的内部计数只允许 0 或 1,
release()
在已为 1 时行为未定义(libstdc++ 直接 abort)。而
counting_semaphore
允许计数累积,只要不超过模板参数
N
所隐含的位宽上限(例如
最大支持 65535)。 所以不要因为“只需要互斥”就默认选
binary_semaphore
——如果你的场景存在“先批量释放再统一获取”(比如生产者攒够 N 个任务才通知消费者),那只有
counting_semaphore
能自然表达。 性能上,
binary_semaphore
在部分平台可能有更轻量的底层实现(如基于 futex 的单比特操作) 可读性上,用
binary_semaphore
能向协作者明确传达“这里只允许 0/1 状态” 二者都不能跨进程共享,仅限同一进程内线程间同步 Windows 和 Linux 下的兼容性现实 libc++(Clang)和 libstdc++(GCC ≥11)均已实现
std::counting_semaphore
,但依赖系统原语:
libstdc++
在 Linux 用
futex
,在 Windows 上回退到
CRITICAL_SECTION
+ 自旋;
libc++
在 Windows 需要
Winelib
支持,否则编译失败。 如果你的目标平台包含老旧 Android(NDK r21 及更早)或某些嵌入式 STL,很可能压根没实现该类——此时别硬套,改用
std::mutex
+
std::condition_variable
手写计数逻辑更稳妥。 检查是否可用:#ifdef __cpp_lib_semaphore 判断语言特性宏 避免在信号处理函数中调用任何 semaphore 方法(非异步信号安全) 调试时注意:GDB 对 semaphore 内部状态通常不友好,建议用日志打点
acquire
/
release
调用位置 实际用起来最麻烦的往往不是语法,而是想清楚“谁负责归还”和“归还时机是否覆盖所有路径”,尤其是涉及异常、early-return、lambda 捕获的场景。

相关文章