现代 C++ 实战(13):C++20 同步原语
第 12 篇用 thread、mutex、condition_variable 搭好了并发地基。C++20 补上最后几块拼图:jthread 自动 join 与协作式取消、semaphore 限流、latch / barrier 多线程阶段同步——写法更短、语义更清晰。
这一篇对应 demo:ref/cpp_demo/concurrency/sync_primitives/。
这是「现代 C++ 实战」系列的第 13 篇。建议先读 第 12 篇:多线程基础。
一、C++20 补上了什么?
| 原语 | 解决的问题 |
|---|---|
std::jthread |
忘记 join() 导致 terminate;需要优雅停止后台线程 |
std::counting_semaphore |
限制同时访问资源的线程数(连接池、限流) |
std::latch |
一次性「等所有人到齐」 |
std::barrier |
可重复的多阶段同步点 |
std::stop_token |
协作式取消(配合 jthread) |
编译要求:GCC 10+、Clang 10+、MSVC 2019+,-std=c++20。
二、std::jthread:自动 join + stop_token
std::thread 析构时若未 join/detach 会 std::terminate。jthread 析构时自动 join。
1 |
|
std::thread |
std::jthread |
|
|---|---|---|
| 析构 | 未 join → terminate | 自动 join |
| 取消 | 无标准机制 | request_stop() + stop_requested() |
| 适用 | 简单 fire-and-forget(需手动 join) | 后台服务、线程池 worker |
协作式取消:线程必须主动检查 stop_requested(),不能强制杀线程——与 第 12 篇 的 mutex 保护一样,是「约定式」安全退出。
三、std::stop_token 与 std::stop_source
1 | std::stop_source src; |
stop_source:发起取消的一方(通常是主线程或管理器)stop_token:只读视图,传给工作线程- 多个
jthread可共享同一个stop_source,一次request_stop()全部通知
四、std::counting_semaphore:资源计数与限流
信号量维护一个非负计数:
acquire():计数减 1;若为 0 则阻塞release():计数加 1,唤醒等待者
1 |
|
| 类型 | 等价 | 用途 |
|---|---|---|
counting_semaphore<N> |
计数上限 N | 连接池、限流、多槽位资源 |
binary_semaphore |
counting_semaphore<1> |
类似 mutex,但可 release 由不同线程 |
与 mutex 对比:mutex 同一时刻只允许 1 个线程;semaphore 允许 k 个——典型场景是「数据库连接池最多 10 条连接」。
五、std::latch:一次性倒计时
1 |
|
- 构造时设初始计数(如 worker 数量)
- 每个线程
count_down()一次 - 所有线程
wait()直到计数为 0 - 只能用一次——适合「全员初始化完毕再开工」
用 C++11 的 mutex + condition_variable 也能写同样逻辑,但 latch 语义专一、代码更短(demo 中有对比)。
六、std::barrier:可重复的多阶段同步
1 |
|
latch |
barrier |
|
|---|---|---|
| 重用 | 一次性 | 每轮 arrive_and_wait 后重置 |
| 典型场景 | 启动屏障 | 并行算法的多轮迭代 |
| 回调 | 无 | 可有 completion_function |
适合:并行排序的分段归并、迭代仿真中「每步全员同步再继续」。
七、与 C++11 原语:什么时候升级?
| 需求 | C++11 | C++20 推荐 |
|---|---|---|
| 互斥保护共享数据 | mutex |
仍用 mutex |
| 等待条件 / 队列 | condition_variable |
仍适用;复杂队列可保留 |
| 后台线程 + 优雅退出 | thread + 原子标志 |
jthread + stop_token |
| 限制并发数 | 手写计数 + cv | counting_semaphore |
| 全员到齐(一次) | mutex + cv + 计数 | latch |
| 多阶段同步 | 同上,易错 | barrier |
原则:C++11 原语并未过时;C++20 是在常见模式上提供专用类型,减少样板代码和 bug。
八、demo 运行
1 | cd ref/cpp_demo/concurrency/sync_primitives |
程序会依次演示 jthread、semaphore、latch、barrier,以及与 C++11 mutex/cv 的对比。启动时会打印编译器对各特性的支持情况(✓/✗)。
九、小结
| 组件 | 一句话 |
|---|---|
jthread |
自动 join,配合 stop_token 协作取消 |
semaphore |
计数限流,控制并发访问数 |
latch |
一次性「等 N 个线程就绪」 |
barrier |
可重复的多阶段同步点 |
现代 C++ 实战系列第 13 篇完。下一篇 线程池与背压控制——有界队列才是生产级关键。
系列导航
| 篇号 | 标题 | 状态 |
|---|---|---|
| 12 | 多线程基础 | ✅ |
| 13 | C++20 同步原语(本篇) | ✅ |
| 14 | 线程池与背压控制 | 下一篇 |
完整大纲见工作区 docs/CPP_SERIES_OUTLINE.md。










