future 与 async 异步编程
定义与作用
std::async 异步执行任务并返回 std::future 获取结果。std::promise/std::packaged_task 提供更灵活的结果传递机制。
auto future = std::async(std::launch::async, []() {
return heavyComputation();
});
auto result = future.get(); // 阻塞直到结果就绪
核心原理
future/promise 通信模型
async 启动策略
完整示例
示例一:飞翔科技并行数据聚合
场景说明:白歌用 async 并行查询多个数据库并聚合结果。
#include <iostream>
#include <future>
#include <vector>
#include <string>
#include <chrono>
#include <thread>
#include <iomanip>
#include <numeric>
// 模拟数据库查询
struct QueryResult {
std::string serverName;
uint64_t recordCount;
double avgLoad;
int latency_ms;
};
QueryResult queryDatabase(const std::string& name, int complexity) {
auto start = std::chrono::steady_clock::now();
// 模拟耗时查询
std::this_thread::sleep_for(std::chrono::milliseconds(complexity));
auto end = std::chrono::steady_clock::now();
int lat = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
return {name, static_cast<uint64_t>(complexity * 1000), complexity * 0.5, lat};
}
int main() {
std::cout << "=== 飞翔科技并行数据聚合 ===\n\n";
auto startAll = std::chrono::steady_clock::now();
// 使用 async 并行查询多个数据库
auto f1 = std::async(std::launch::async, queryDatabase, "DB-User", 300);
auto f2 = std::async(std::launch::async, queryDatabase, "DB-Order", 500);
auto f3 = std::async(std::launch::async, queryDatabase, "DB-Product", 200);
auto f4 = std::async(std::launch::async, queryDatabase, "DB-Log", 400);
// 主线程可继续其他工作
std::cout << "所有查询已发出,主线程可执行其他任务...\n\n";
// 收集结果
auto r1 = f1.get();
auto r2 = f2.get();
auto r3 = f3.get();
auto r4 = f4.get();
auto endAll = std::chrono::steady_clock::now();
int totalMs = std::chrono::duration_cast<std::chrono::milliseconds>(endAll - startAll).count();
std::vector<QueryResult> results = {r1, r2, r3, r4};
// 输出结果
std::cout << std::left
<< std::setw(14) << "数据库"
<< std::setw(12) << "记录数"
<< std::setw(10) << "平均负载"
<< std::setw(12) << "查询耗时"
<< "\n";
std::cout << std::string(48, '-') << "\n";
uint64_t totalRecords = 0;
double totalLoad = 0;
for (const auto& r : results) {
std::cout << std::left
<< std::setw(14) << r.serverName
<< std::setw(12) << r.recordCount
<< std::setw(10) << std::fixed << std::setprecision(1)
<< r.avgLoad << "%"
<< std::setw(8) << (std::to_string(r.latency_ms) + "ms")
<< "\n";
totalRecords += r.recordCount;
totalLoad += r.avgLoad;
}
std::cout << std::string(48, '-') << "\n";
std::cout << std::left
<< std::setw(14) << "合计"
<< std::setw(12) << totalRecords
<< std::setw(10) << std::fixed << std::setprecision(1)
<< totalLoad << "%"
<< "\n";
std::cout << "\n总耗时: " << totalMs << "ms";
int sequentialMs = 300 + 500 + 200 + 400;
std::cout << " (串行预估: " << sequentialMs << "ms)";
std::cout << "\n加速比: " << std::fixed << std::setprecision(1)
<< (static_cast<double>(sequentialMs) / totalMs) << "x\n";
}
预期输出:
=== 飞翔科技并行数据聚合 ===
所有查询已发出,主线程可执行其他任务...
数据库 记录数 平均负载 查询耗时
------------------------------------------------
DB-User 300000 150.0% 300ms
DB-Order 500000 250.0% 500ms
DB-Product 200000 100.0% 200ms
DB-Log 400000 200.0% 400ms
------------------------------------------------
合计 1400000 700.0%
总耗时: XXXms (串行预估: 1400ms)
加速比: X.Xx
逐段分析:
std::launch::async强制创建新线程异步执行future.get()会阻塞,若任务尚未完成则等待- 并行执行时间约为最慢任务的时间,而非四者之和
- 默认策略
async|deferred由实现决定,不确定时显式指定
示例二:promise 与 packaged_task
场景说明:孔蓝用 promise 实现带回调的异步任务。
#include <iostream>
#include <future>
#include <thread>
#include <string>
#include <chrono>
#include <functional>
// 使用 promise 实现"一次性通知"模式
void deployService(std::promise<std::string>&& prom,
const std::string& serviceName) {
std::cout << " [部署] 开始部署 " << serviceName << "...\n";
// 模拟部署过程
for (int i = 1; i <= 3; ++i) {
std::this_thread::sleep_for(std::chrono::milliseconds(200));
std::cout << " [部署] " << serviceName << " 进度: " << (i * 33) << "%\n";
}
std::string result = serviceName + " 部署成功 (v2.0.1)";
prom.set_value(result); // 设置结果,唤醒 future.get()
}
// 使用 packaged_task 包装可调用对象
std::string buildProject(const std::string& project, int duration) {
std::this_thread::sleep_for(std::chrono::milliseconds(duration));
return project + " 构建完成";
}
int main() {
std::cout << "=== 飞翔科技部署流水线 ===\n\n";
// 1. promise 模式
std::promise<std::string> deployPromise;
std::future<std::string> deployFuture = deployPromise.get_future();
std::thread deployThread(deployService,
std::move(deployPromise),
"飞翔-API-Gateway");
std::cout << " 部署任务已提交,等待结果...\n";
std::string deployResult = deployFuture.get(); // 阻塞等待
std::cout << " 结果: " << deployResult << "\n\n";
deployThread.join();
// 2. packaged_task 模式
std::packaged_task<std::string(const std::string&, int)> buildTask(buildProject);
std::future<std::string> buildFuture = buildTask.get_future();
std::cout << " 构建任务已提交...\n";
std::thread buildThread(std::move(buildTask), "飞翔-Web", 600);
std::string buildResult = buildFuture.get();
std::cout << " 结果: " << buildResult << "\n";
buildThread.join();
// 3. shared_future(多个消费者)
std::cout << "\n--- shared_future 广播 ---\n";
std::promise<int> sharedPromise;
std::shared_future<int> sf = sharedPromise.get_future().share();
auto consumer = [](std::shared_future<int> f, const std::string& name) {
int val = f.get();
std::cout << " [" << name << "] 收到通知: " << val << "\n";
};
std::future<void> c1 = std::async(std::launch::async, consumer, sf, "李眉");
std::future<void> c2 = std::async(std::launch::async, consumer, sf, "黄俪");
std::future<void> c3 = std::async(std::launch::async, consumer, sf, "小崔");
std::this_thread::sleep_for(std::chrono::milliseconds(100));
sharedPromise.set_value(42); // 一次设置,三个消费者都能获取
c1.get(); c2.get(); c3.get();
std::cout << "\n所有流水线任务完成\n";
}
预期输出:
=== 飞翔科技部署流水线 ===
部署任务已提交,等待结果...
[部署] 开始部署 飞翔-API-Gateway...
[部署] 飞翔-API-Gateway 进度: 33%
[部署] 飞翔-API-Gateway 进度: 66%
[部署] 飞翔-API-Gateway 进度: 99%
结果: 飞翔-API-Gateway 部署成功 (v2.0.1)
构建任务已提交...
结果: 飞翔-Web 构建完成
--- shared_future 广播 ---
[李眉] 收到通知: 42
[黄俪] 收到通知: 42
[小崔] 收到通知: 42
所有流水线任务完成
逐段分析:
promise是"推"模型:工作线程set_value,调用线程getpackaged_task包装可调用对象,自动创建 futureshared_future允许多个消费者等待同一结果,通过share()从future转换promise必须move传递给工作线程(不可拷贝)future.get()只能调用一次,第二次调用会抛异常
易错场景与面试考点
易错场景
1. future.get() 只能调用一次
auto f = std::async(work);
auto r1 = f.get(); // OK
auto r2 = f.get(); // ❌ 抛出 std::future_error
// 解决:在 get 前调用 f.valid() 检查,或用 shared_future
2. 不保存 async 返回值
std::async(std::launch::async, heavyWork); // ❌ future 立即析构
// future 析构会阻塞直到任务完成!失去异步意义
auto f = std::async(std::launch::async, heavyWork); // ✅ 保存
3. promise 生命周期
std::future<int> f;
{
std::promise<int> p;
f = p.get_future();
} // ❌ promise 销毁,但 future 还活着
// 后续 f.get() → broken promise 异常
4. deferred 策略陷阱
// 如果实现选择了 deferred,任务在 get() 执行于当前线程
auto f = std::async(workWithThreadLocal);
f.get(); // 可能不在新线程执行,thread_local 值可能不符预期
面试考点
| 考点 | 要点 |
|---|---|
| future/promise 模型 | promise 端写,future 端读,通过共享状态通信 |
| async 策略 | launch::async(新线程)、launch::deferred(延迟)、默认(实现决定) |
| packaged_task | 包装可调用对象,自动关联 future |
| shared_future | 可多次 get,多消费者场景 |
| future.get() | 只能调用一次,之后 valid() 为 false |
| future.wait() | 等待但不获取值 |
| future.wait_for/until | 带超时的等待 |
| broken_promise | promise 未 set_value 就析构 |