C++ std::counting_semaphore _ C++20原生信号量用法【实战】

浅浩君_7960

浅浩君_7960

2026-04-13

369人浏览

原创

std::counting_semaphore是c++20无锁计数信号量,初始化值须为≥0的编译期常量;acquire/release可跨线程调用,需防死锁、加超时、勿误用binary_semaphore别名,且不保证唤醒顺序。

c++ std::counting_semaphore _ c++20原生信号量用法【实战】

std::counting_semaphore 是 C++20 原生、轻量、无锁的计数信号量,但不是万能互斥替代品,用错场景或参数会直接编译失败或逻辑死锁。

初始化值必须非负,且是编译期常量

构造 std::counting_semaphore 时传入的初始值必须是 ≥ 0 的字面量或 constexpr 表达式。传负数、变量、运行时计算值都会触发编译错误,比如:

  • std::counting_semaphore sem(-1); → 编译失败:“non-negative value required”
  • int n = 3; std::counting_semaphore sem(n); → 编译失败:模板参数需为常量表达式
  • 正确写法:std::counting_semaphore sem(3); 或 constexpr int init = 2; std::counting_semaphore sem(init);

注意:模板参数(如 )是最大允许计数值,不是初始值;初始值由构造函数参数指定,且不能超过该上限。

acquire() 和 release() 不必成对出现在同一线程

这是信号量区别于 std::mutex 的关键设计。你完全可以在线程 A 中调用 release(),在线程 B 中调用 acquire() —— 它们通过共享的计数器通信,而非所有权。

  • 典型误用:acquire() 后忘记 release() → 计数器持续为 0,其他线程永久阻塞
  • 反模式:acquire() 和 release() 在同一线程内“配对”使用,却未配合临界区保护 → 失去同步意义
  • 正确解耦场景:生产者线程 release() 通知有新数据,消费者线程 acquire() 等待数据就绪

尤其注意:没有“自动 RAII 封装”,必须手动确保 release() 被执行(比如用 std::scoped_lock 思路自己封装,或配合 try_acquire_for() 防死锁)。

C++14
C++14

C++14 对 C++11 的修正与增强版本,适合旧系统维护和较老工具链兼容。

下载

超时获取必须用 try_acquire_for() 或 try_acquire_until()

acquire() 是无条件阻塞的,一旦等待对象永远不 release(),线程就卡死。真实系统中必须加超时防护。

  • sem.try_acquire_for(std::chrono::milliseconds(500)):最多等 500ms,返回 bool
  • sem.try_acquire_until(std::chrono::steady_clock::now() + std::chrono::seconds(2)):等至绝对时间点
  • 不要试图用 while(!sem.try_acquire()) std::this_thread::yield(); 轮询 —— 浪费 CPU,且不保证唤醒时机

注意:try_acquire()(无超时)只是立即返回成功/失败,不等待;它适合纯非阻塞路径,比如事件驱动中的快速试探。

binary_semaphore 是 counting_semaphore 的别名,别混用模板参数

std::binary_semaphore 是类型别名,等价于 std::counting_semaphore。但它不接受构造参数以外的模板参数 —— 下面写法全错:

  • std::binary_semaphore sem(1); → 编译错误:“binary_semaphore is not a template”
  • std::counting_semaphore sem(0); ✅ 正确,但和 std::binary_semaphore sem(0); 等价
  • 想限制“最多 N 个并发”,必须用 std::counting_semaphore<n></n>,而不是 binary_semaphore

混淆这点会导致编译器报错“counting_semaphore is not a member of std”,尤其是旧编译器未启用 C++20 或遗漏 #include <semaphore></semaphore> 时,错误信息容易误导人去查模板参数而非编译环境。

最易被忽略的一点:信号量不保证唤醒顺序。多个线程同时在 acquire() 等待时,谁先被唤醒由实现决定(通常是 FIFO,但标准不保证),若业务逻辑依赖严格调度顺序(如 ABC 打印),必须靠多个信号量接力控制,单靠一个信号量无法实现。

C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

c++

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

2023.10.24

6104

49

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

2023.09.25

354

5

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.02

6459

3

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.08.29

3145

6

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

2025.08.29

4048

10

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

2025.08.29

2785

10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

2023.08.10

4258

6

Playwright 测试失败怎么办
Playwright 测试失败怎么办

Playwright测试失败专题汇集超时、定位器匹配、浏览器提前关闭、依赖与插件配置、文件上传、SSL及Docker启动等故障文章,并介绍通过截图、报告和Trace定位错误。

2026.10.21

20

15

Playwright 页面操作指南
Playwright 页面操作指南

Playwright页面操作专题汇集元素定位、点击与输入、等待响应、多页面切换、Iframe、事件监听及文件处理的已审核文章,便于按操作场景查阅示例与常见问题。

2026.10.21

0

16

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C++ Core Guidelines
C++ Core Guidelines

共0课时 | 0人学习

C++ Reference
C++ Reference

共0课时 | 0人学习

C++ 官方标准说明
C++ 官方标准说明

共0课时 | 0人学习