std::async / std::future:更高级的异步任务
std::async / std::future:异步任务
🔷 一、为什么需要它?
std::thread 的问题:启动线程后拿不到返回值。
cpp
// ❌ thread 无法返回值
int result;
std::thread t([&result]() {
result = 42; // 只能靠副作用"传出"结果,很麻烦
});
t.join();
std::async 解决了这个问题:启动异步任务,返回一个 future 对象,将来可以从中取结果。
🔷 二、基本用法
cpp
#include <future>
#include <iostream>
int heavyCalc(int x) {
// 模拟耗时计算
std::this_thread::sleep_for(std::chrono::seconds(2));
return x * x;
}
int main() {
// 启动异步任务,立即返回 future
std::future<int> fut = std::async(std::launch::async, heavyCalc, 10);
std::cout << "任务已启动,主线程继续干别的..." << std::endl;
// 需要结果时再 get(),会阻塞直到任务完成
int result = fut.get();
std::cout << "结果:" << result << std::endl; // → 100
}
future就像一张”取货凭证”:任务在后台跑,你先忙别的,需要结果时凭证取货。
🔷 三、launch 策略
cpp
// 强制新线程运行 std::async(std::launch::async, task); // 延迟执行:调用 get() 时才在当前线程运行(不开新线程) std::async(std::launch::deferred, task); // 默认:由系统决定(通常等同于 async) std::async(task);
实际使用几乎总是用 std::launch::async,行为明确可控。
🔷 四、异常传播
future 能自动捕获异步任务中的异常,在 get() 时重新抛出:
cpp
auto fut = std::async(std::launch::async, []() -> int {
throw std::runtime_error("任务出错了!");
return 0;
});
try {
int val = fut.get(); // 异常在这里重新抛出
} catch (const std::exception& e) {
std::cout << "捕获异常:" << e.what(); // → "任务出错了!"
}
🔷 五、并行加速:多个 future 同时跑
cpp
// 串行:总共耗时 3s int a = heavyCalc(3); // 1s int b = heavyCalc(4); // 1s int c = heavyCalc(5); // 1s // ✅ 并行:总共只耗时 ~1s auto fa = std::async(std::launch::async, heavyCalc, 3); auto fb = std::async(std::launch::async, heavyCalc, 4); auto fc = std::async(std::launch::async, heavyCalc, 5); // 三个任务同时在跑,最后统一取结果 int sum = fa.get() + fb.get() + fc.get(); // → 9 + 16 + 25 = 50
🔷 六、promise:手动控制 future
async 是自动的;promise 让你手动决定何时把值”放入” future:
cpp
#include <future>
std::promise<int> prom;
std::future<int> fut = prom.get_future();
// 在另一个线程中设置值
std::thread t([&prom]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
prom.set_value(42); // 把值"推入" future
});
std::cout << fut.get(); // 阻塞直到 set_value 被调用 → 42
t.join();
适用场景:回调式 API、事件驱动、手动控制完成时机。
🔷 七、shared_future:结果共享给多个消费者
普通 future 只能 get() 一次,shared_future 可以多次读取:
cpp
std::shared_future<int> sf = std::async(std::launch::async, []() {
return 100;
}).share(); // 转为 shared_future
// 多个线程都可以读同一个结果
std::thread t1([sf]() { std::cout << sf.get(); });
std::thread t2([sf]() { std::cout << sf.get(); });
t1.join(); t2.join();
// 两个线程都输出 100,安全!
🔷 八、整体结构图
启动 取结果
你 ──► async(task) ──► future ──► get() ──► 结果/异常
│
└──► 后台线程默默运行 task...
手动控制:
你 ──► promise ──► get_future() ──► future
│
└──► 任意时刻 set_value() 触发
🔷 选择指南
| 场景 | 用什么 |
|---|---|
| 简单异步任务,需要返回值 | std::async |
| 多任务并行,最后汇总 | 多个 std::future |
| 回调/事件驱动,手动控制完成 | std::promise |
| 多个消费者共享同一结果 | std::shared_future |
| 不需要返回值,纯副作用 | std::thread |
下一步可以学:
std::packaged_task:把函数包装成可调度的任务- 线程池:复用线程,避免频繁创建销毁的开销
- C++20 协程:更现代的异步方式(
co_await)
