线程管理的基础

启动线程

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_strongcompare_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()。

std::promise

参考:thread 构造;thread 析构;future。