异步机制
异步机制
异步把“提交工作”和“取得结果”分开:调用者可以先推进其他工作,稍后再等待结果。它并不保证一定创建线程,也不能替代取消、限流和任务生命周期设计。
核心对象与关系
1 | 产生结果的一端 使用结果的一端 |
一个 std::future<T> 通常只有一个消费者:get() 只能调用一次。需要让多个消费者读取同一结果时,将 future 转换为 std::shared_future<T>。
| 类型 | 何时使用 | 结果与异常 |
|---|---|---|
std::async |
简单的一次性计算 | 自动建立 future 共享状态 |
std::promise<T> |
一个执行路径手动向另一路径报告结果 | set_value / set_exception |
std::packaged_task |
将可调用对象和 future 分离,常用于任务队列 | 调用 task 时填充结果 |
std::future<T> |
单次取得或等待结果 | get() 会重新抛出任务异常 |
std::async 与启动策略
函数原型(概念形式):
1 | template<class F, class... Args> |
function 和 args 是任务及其参数;返回的 future 关联任务的结果。参数会被保存,引用语义需显式 std::ref,并仍需保证生命周期。
1 |
|
| 策略 | 任务何时执行 | 注意 |
|---|---|---|
std::launch::async |
由实现异步执行,通常使用新线程 | 不应假设它是可复用线程池 |
std::launch::deferred |
第一次 get() 或 wait() 时,在调用这些函数的线程执行 |
wait_for 可返回 deferred |
| 未指定策略 | 实现可选择上述任一方式 | 若确实需要异步,显式指定 async |
不要丢弃 std::async(std::launch::async, ...) 返回的临时 future:关联异步任务的 future 在临时对象析构时可能等待任务完成,表面上的“并发调用”会退化为串行。应保存 future,并在明确位置 get() 或 wait()。
等待、超时与异常
| 成员函数 | 结果 | 注意 |
|---|---|---|
get() |
等待、取走值,并重新抛出异常 | 仅一次;之后 valid() 为 false |
wait() |
只等待完成 | 不取走值,之后仍可 get() |
wait_for() / wait_until() |
返回 ready、timeout 或 deferred |
超时不取消任务 |
valid() |
是否仍关联共享状态 | 对已 get 或移动后的 future 为 false |
1 |
|
promise:手动交付值或异常
promise 和它产生的 future 共享一个结果槽。每个 promise 只能 get_future() 一次,通常只能成功设置一次值或异常;违反约束会抛出 std::future_error。
1 |
|
若 promise 在未设置结果时被销毁,future 的 get() 会抛出 std::future_error(broken promise)。这通常意味着异常或取消路径漏掉了交付结果。
packaged_task 与 shared_future
packaged_task<R(Args...)> 包装可调用对象;调用该 task 时,返回值或异常会自动写入其 future。它适合由队列的消费者执行任务、由提交者保留 future 的结构。
1 |
|
多个读取者需要同一份结果时:
1 | std::future<int> one_reader = std::async(std::launch::async, [] { return 123; }); |
选择与边界
- 只需一个简单计算结果:
std::async(std::launch::async, ...)可读性高。 - 线程已由你管理、需要手动报告结果或异常:
promise+future。 - 要构建有界任务队列:用
packaged_task表达任务;队列的同步、停止和背压仍需自行设计,见同步机制。 future没有标准的通用取消 API。超时只说明当前未完成,不能安全终止任务;应设计协作式停止协议(C++20 可用stop_token)。
常见误区与实践建议
std::async不是线程池;对大量短任务反复调用可能产生明显调度开销,需基准测试。get()既是同步点也是异常传播点,调用处必须准备处理异常。- 不要让 UI/事件循环线程在不合适的位置阻塞
get();可以先检查、回调或把等待放到合适的工作路径。 - 若任务访问外部对象,future 的存在不延长那些对象的生命周期;用值捕获、共享所有权或明确
join保证安全。
编译命令:c++ -std=c++17 -Wall -Wextra -Wpedantic -pthread file.cpp。



