C++11 并发新特性(thread, atomic, condition_variable, thread_local),异常与错误处理
C++11 并发新特性(thread, atomic, condition_variable, thread_local),异常与错误处理
一、std::thread——C++11的线程
1.1基本用法
#include<iostream>
#include<thread>
using namespace std;
void work(int n){
cout<<"子线程工作:"<< n <<endl;
}
int main(){
thread t(work, 5);//创建线程并传参
t.join();//等待线程执行完
cout<<"主线程结束"<<endl;
}
👉 std::thread 构造函数的第一个参数是函数指针,后面跟参数。
1.2 join vs detach
- join(): 阻塞主线程,等待子线程执行结束。
- detach(): 让子线程后台执行,主线程不再等待(常用于守护线程)。
thread t1(work, 1);
t1.join(); //等待结束
thread t2(work, 2);
t2.detach();//后台运行,与t2对象分离,由系统自动回收资源
// 错误做法:既不join也不detach
std::thread t3(work, 3);
// t3销毁时会调用terminate(),导致程序异常终止
⚠注意:如果线程对象销毁时还没 join 或 detach,会抛异常,必须二选一。
二、std::atomic——原子操作
2.1问题背景
多线程同时访问一个变量时可能出现竞态条件:
#include <iostream>
#include <thread>
using namespace std;
int counter = 0;
void add(){
for(int i = 0; i < 1000000; i++) counter++;
}
int main(){
thread t1 (add), t2(add);
t1.join(); t2.join();
cout<<"counter = "<<counter<<endl; //结果可能小于2000000, 我的运行结果是1245112
}
👉 因为 counter++ 不是原子操作。
2.2解决方案:atomic
#include <atomic>
atomic<int> counter(0);
void add() {
for (int i = 0; i < 1000000; i++) counter++;
}
👉 atomic 保证 ++ 是原子操作,线程安全。
2.3常用方法
2.3.1 fetch_add(x): 原子加
功能:将原子变量的值加上 x,返回操作前的原始值(类似 x += n 的原子版本)。
线程安全:整个 “读取 - 修改 - 写入” 过程是原子的,不会被其他线程中断。
atomic<int> count(0);
int old_val = count.fetch_add(1); // 等价于原子版 count++,返回操作前的值
int old_val2 = count.fetch_add(2); // 原子版 count += 2
2.3.2 fetch_sub(x): 原子减
原子减同上原子加。
2.3.3 compare_exchange_strong(expected, desired): CAS 强操作(无锁编程核心)
功能:CAS(Compare-And-Swap)是无锁编程的核心操作,逻辑如下:
1.比较原子变量的当前值与 expected(预期值)。
2.如果相等,将原子变量的值更新为 desired(目标值),返回 true。
3.如果不相等,将 expected 更新为原子变量的当前值,返回 false。
“强” 的含义:即使没有其他线程干扰,也可能因硬件原因失败(如某些架构的伪失败),但 strong 版本会持续重试直到成功或确实不匹配。
- compare_exchange_weak
std::atomic<int> val(5);
int expected = 5;
// 尝试将 val 从 5 改为 10
bool success = val.compare_exchange_strong(expected, 10);
// 如果成功:val=10,success=true
// 如果失败:expected被更新为val的当前值,success=false
2.3.4 compare_exchange_weak(expected, desired) 弱操作
功能:与 strong 版本逻辑相同,但允许在无竞争时也可能返回失败(伪失败)。
适用场景:循环中使用(失败后可重试),性能通常比 strong 版本更高。
std::atomic<int> val(5);
int expected = 5;
// 循环确保最终成功(除非被其他线程修改)
while (!val.compare_exchange_weak(expected, 10)) {
// 失败时,expected已被更新为当前值,无需手动重置
}
例如,用 compare_exchange_weak 实现一个线程安全的自增操作:
std::atomic<int> num(0);
void safe_increment() {
int expected = num.load();
while (!num.compare_exchange_weak(expected, expected + 1)) {
// 循环重试直到成功
}
}
👉这与直接使用 num++ 效果相同。
三、条件变量condition_variable
3.1问题背景
多个线程需要同步,例如生产者-消费者模型。
3.2基本用法
#include<iostream>
#include<thread>
#include<mutex>
#include<condition_variable>
#include<queue>
using namespace std;
queue<int> q;
mutex mtx; //互斥锁,保证对共享队列的操作是线程安全的
condition_variable cv; //条件变量,用于线程间的等待和通知机制
void producer(){
for(int i = 1; i <= 5; i++){
unique_lock<mutex> lock(mtx);
q.push(i);
cout<<"生产:"<< i <<endl;
}
cv.notify_one(); //唤醒一个消费者的等待线程,再通过下面的代码让出cpu
this_thread::sleep_for(chrono::milliseconds(100));
}
void consumer(){
for(int i = 1; i <= 5; i++){
unique_lock<mutex> lock(mtx);
cv.wait(lock, []{return !q.empty();}); //等待条件成立
int val = q.front();
q.pop();
cout<<"消费:"<<val<<endl;
}
}
int main(){
thread t1(producer), t2(consumer);
t1.join();
t2.join();
}
四、thread_local——线程局部存储
4.1问题背景
- 全局变量在多个线程中会被共享,有时我们需要每个线程独有的副本。
- 这时thread_local的作用就出来了,它是线程间不共享的,互不干扰。
4.2用法
#include<iostream>
#include<thread>
using namespace std;
thread_local int counter = 0;
void work(int id){
counter++;
cout<<"线程"<<id<<"counter = "<<counter<<endl;
}
int main(){
thread t1(work,1);
thread t2(work,2);
t1.join();
t2.join();
}
👉 thread_local 修饰的变量在每个线程里都有独立的副本,互不干扰。
五、异常与错误处理
5.1异常处理:std::exception_ptr
5.1.1问题背景
C++11 之前,异常只能在单线程内传播。
但多线程程序中,一个线程抛出异常,可能需要在主线程里捕获。
5.1.2std::exception_ptr
C++11 提供std::exception_ptr来存储并传递异常对象。
#include<iostream>
#include<thread>
#include<exception>
using namespace std;
exception_ptr gException;
void worker(){
try{
throw runtime_error("子线程出错!");
}catch(...){
gException = current_exception();//捕获当前异常
}
}
int main(){
thread t(worker);
t.join();
if(gException){
try{
rethread_exception(gException);//在主线程重新抛出
} catch(const exception& e){
cout<<"主线程捕获异常:"<<e.what()<<endl;
}
}
}
👉 这样可以跨线程传递异常。
5.2 错误处理:std::error_code / std::error_category
5.2.1 问题背景
传统的errno(全局变量)不够灵活,error_code 是 C++11 引入的标准化错误处理机制。包含:错误值(value),错误信息(message),错误类别(category)。
5.2.2 error_code
#include<iostream>
#include<system_error>
using namespace std;
int main(){
error_code ec = make_error_code(errc::invalid_argumen);//errc::invalid_argumen为标准错误枚举值
//make_error_code() 函数将这个枚举值转换为 error_code 对象
cout<<"错误码:"<<ec.value()<<endl;
cout<<"错误信息:"<<ec.message()<<endl;
cout<<"错误类别:"<<ec.category()<<endl;//category区分错误来源(如系统调用错误、自定义错误等)。
}
👉 好处:比异常更轻量,不中断程序执行。
👉 用法:标准库 I/O、文件系统(C++17 )常用。
5.3协程coroutine (C++20 正式标准化,C++11/14/17有experimental)
5.3.1 问题背景
- 线程开销大(上下文切换耗时)。
- 协程是“轻量级线程”,可以在一个线程里挂起和恢复函数执行。
- 特别适合:异步I/O,事件驱动,任务调度。
5.3.2 C++20协程示例
#include <iostream>
#include <coroutine>
using namespace std;
struct Task {
struct promise_type {
Task get_return_object() { return {}; } //返回协程的结果对象(这里返回 Task 实例)。
suspend_never initial_suspend() { return {}; }// 指定协程启动后是否立即暂停。suspend_never{} 表示不暂停,协程会立即开始执行。
suspend_never final_suspend() noexcept { return {}; }//指定协程结束前是否暂停。suspend_never{} 表示不暂停,协程结束后直接退出。
void return_void() {}//return_void():协程无返回值时的处理(对应 co_return;)。
void unhandled_exception() {}//处理协程中未捕获的异常。
};
};
Task myCoroutine() {
cout << "协程开始" << endl;
//执行 co_await suspend_always{}:suspend_always 表示必须暂停,此时协程会暂停执行,控制权返回给调用者(main 函数)。
co_await suspend_always{};
cout << "协程恢复" << endl;
}
int main() {
auto t = myCoroutine();
cout << "main 结束" << endl;
}
5.3.3协程的特点
- 用 co_return 返回值
- 用 co_yield 生成值(像 Python 的 yield 生成器)
- 用 co_await 挂起/恢复
5.3.4 协程 vs 线程
| Column 1 | Column 2 | Column 2 |
|---|---|---|
| 特性 | 协程 | 线程 |
| 开销 | 轻量级(栈共享) | 重(独立站+OS调度) |
| 切换 | 用户态(快) | 内核态(慢) |
| 数量 | 可达百万级 | 通常几千个 |
| 场景 | I/O密集、异步 | CPU密集 |
小结
- std::thread:创建、join、detach,支持 lambda
- std::atomic:原子操作,解决数据竞争
- condition_variable:线程同步(生产者-消费者)
- thread_local:线程局部存储
- 1.异常处理
- exception_ptr:跨线程传递异常
- current_exception 捕获,rethrow_exception 抛出
- 2.错误处理
- error_code:错误码对象,代替 errno
- error_category:描述错误来源
- 3.协程
- 轻量级线程
- 三个关键字:co_return、co_yield、co_await
- 适合异步 I/O / 任务调度
更多推荐

所有评论(0)