线程管理的基础
启动线程
std::thread 可传函数、函数对象、lambda
正确写法
void background_task() { /* ... */ }
std::thread t(background_task); // 传函数指针,不要加 ()
// 或
std::thread t(&background_task); // 显式取地址,等价
函数名在表达式中会 衰变为函数指针,正好符合 std::thread 对 callable 的要求。
歧义写法
background_task是类
class background_task { /* ... */ };
std::thread t(background_task());
// 解析为:声明函数 t,参数类型是「指向无参、返回 background_task 的函数的指针」
// t 不是 std::thread 对象!
background_task是函数
void background_task() { /* ... */ }
std::thread t(background_task());
// 解析为:std::thread t( background_task() );
// ^^^^^^^^^^^^^^^^^
// 表达式:调用 background_task(),得到 void
记忆规则:
| 写法 | background_task 是类 | background_task 是函数 |
|---|---|---|
t(background_task()) | 声明函数 t(静默坑) | 先调用函数,再构造(编译失败) |
t(background_task) | 传临时对象(若可默认构造则可行) | 正确:传函数指针 |
t((background_task())) | 构造临时对象再建线程 | 不适用 |
t{background_task()} | 构造临时对象再建线程 | 不适用(函数不能 {} 初始化)写法 |
线程完成
必须遵守的规则
- 析构前二选一:每个
std::thread在销毁前必须join()或detach(),否则析构函数调用std::terminate()直接终止程序 - 只能 join 一次:
join()后线程与std::thread对象解绑,joinable()变为false,再次join()是错误 - join 会阻塞:当前线程一直等到目标线程执行完;第2章指出它不如 condition_variable / future 灵活(超时、轮询等见第4章)
使用场景与对比
| 方式 | 含义 | 典型场景 |
|---|---|---|
join() | 等待线程结束 | 需要结果、需要保证子线程完成后再继续 |
detach() | 不等待,后台运行 | 「发后即忘」;但主线程不能依赖子线程里的局部数据 |
同步
std::atomic
操作
存储(store)操作,可选用的内存次序有 std::memory_order_relaxed、std::memory_order_release 或 std::memory_order_seq_cst
载入(load)操作,可选用的内存次序有 std::memory_order_relaxed、std::memory_ order_consume、std::memory_order_acquire 或std::memory_order_seq_cst。
“读-改-写”(read-modify-write)操作,可选用的内存次序有 std::memory_order_
relaxed、std::memory_order_consume、std::memory_order_acquire、std::memory_order_release、std::memory_order_acq_rel 或 std::memory_order_seq_cst。
compare_exchange_strong
bool compare_exchange_strong(T& expected, T desire, ...);
- 成功:原子值原等于
expected→ 改为desired,返回true - 失败:原子值已变 →
expected被更新为 当前值,返回false(值未改)
failure 的内存序不能是 release/acq_rel(失败时没有「发布」语义)。
strong vs weak
compare_exchange_strong | compare_exchange_weak | |
|---|---|---|
| 失败原因 | 仅当 当前值 ≠ expected | 同上,还可能 伪失败(spurious failure) |
| 典型用法 | 单次判断、逻辑必须准确 | 放在 while 循环 里重试 |
| 性能 | 某些 CPU 上可能多等一轮 | 可能少一次硬 CAS 指令 |
用法
std::atomic<bool> flag{false};
bool expected = false;
if (flag.compare_exchange_strong(expected, true)) {
// 抢到:原先确实是 false
} else {
// 别人已置 true,expected 已被更新为 true
}
///////// 等价 ///////////
bool expected = false;
while (!b.compare_exchange_weak(expected, true) && !expected) {
// 伪失败或竞争:expected 会被更新,可重试或退出
}
一次性事件
std::future
一次性事件:结果就绪后 不可重置(与条件变量可反复等待不同)
future 是一个模板类,用来等待异步操作的结果。它本身不能直接存值,只能从 promise 或 async 那里获取结果。
核心方法
| 方法 | 作用 |
|---|---|
get() | 阻塞等待,直到结果就绪,然后返回值(只能调用 1 次) |
wait() | 阻塞等待结果就绪,但不返回值 |
wait_for(timeout) | 等待指定时间,超时就返回,不卡死 |
wait_until(time) | 等待到指定时间点 |
valid() | 判断 future 是否还有效 |
关键特性
get()只能调用一次,调用后 future 失效- 没结果时调用
get()会阻塞当前线程,直到结果到来 - 可以安全接收异步线程抛出的异常(传统线程做不到)