线程管理的基础
启动线程
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())) | 构造临时对象再建线程 | 不适用 |
t{background_task()} | 构造可调用临时对象再建线程 | 本例函数返回 void,不能作为 thread 的可调用参数 |
线程完成
必须遵守的规则
- 析构时若
joinable()为 true,会调用std::terminate();默认构造、已移动走、已 join 或 detach 的对象不需要再处理。线程函数执行完毕也不会自动让对象变为不可 join。 - 只能 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 desired, ...);
- 成功:原子值原等于
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
}
///////// 等价 ///////////
expected = false; // 下面是另一种写法,实际使用时与上面的 strong 示例二选一
while (!flag.compare_exchange_weak(expected, true) && !expected) {
// 伪失败或竞争:expected 会被更新,可重试或退出
}
一次性事件
std::future
一次性事件:结果就绪后 不可重置(与条件变量可反复等待不同)
future 是一个模板类,用来等待异步操作的结果。它本身不能直接存值,可从 promise、packaged_task 或 async 关联的共享状态中获取结果。
核心方法
| 方法 | 作用 |
|---|---|
get() | 阻塞等待,直到结果就绪,然后返回值(只能调用 1 次) |
wait() | 阻塞等待结果就绪,但不返回值 |
wait_for(timeout) | 返回 ready、timeout 或 deferred;调度延迟可能使实际等待超过指定时长 |
wait_until(time) | 等待到指定时间点 |
valid() | 判断 future 是否还有效 |
关键特性
get()只能调用一次,调用后 future 失效- 没结果时调用
get()会阻塞当前线程,直到结果到来 std::async、std::packaged_task或promise::set_exception()保存到共享状态中的异常会由get()重新抛出;普通std::thread中未捕获的异常仍会调用std::terminate()。