std::async + std::future 可实现标准库级轻量异步,需显式指定 std::launch::async 确保并发;future 不可拷贝,异常需调用 get() 才抛出;轮询用 wait_for 避免阻塞;promise 适合外部控制完成时机,packaged_task 适用于预绑定函数的一次性执行。

用 std::async + std::future 快速启动异步任务并获取结果
不需要引入第三方库,C++11 起标准库就支持轻量级异步执行。核心是让任务在后台线程跑完,主线程不阻塞,再通过 std::future 拿结果或触发回调。
常见错误是直接调用 get() —— 它会阻塞等待完成,失去“异步”意义;真正需要的是轮询或主动通知机制。
-
std::async默认可能延迟启动(std::launch::deferred),必须显式指定std::launch::async才确保真并发 - 返回的
std::future不能拷贝,只能移动或通过std::shared_future共享(如需多次检查状态) - 若任务抛异常,
get()会重新抛出,但不调用get()就永远捕获不到 —— 这是容易被忽略的陷阱
手动轮询 wait_for 实现非阻塞检查
当无法修改任务逻辑、又不想用线程池时,轮询是最直接的“伪回调”方式:定期查任务是否就绪,就绪后执行处理逻辑。
适合 GUI 主循环、嵌入式主循环等不允许阻塞的场景,但注意别太频繁轮询浪费 CPU。
- 用
future.wait_for(std::chrono::milliseconds(0))瞬时检查,返回std::future_status::ready表示完成 - 不要用
wait_until配合系统时间,易受时钟跳变影响;优先用相对时间(如steady_clock) - 轮询间隔建议 ≥1ms,Windows 下低于 15ms 可能无实际效果(调度粒度限制)
用 std::promise + std::thread 手动控制回调时机
标准库没有内置“任务完成自动调用函数”的机制,但可以用 std::promise 把结果“推”出去,再由持有对应 std::future 的代码决定何时响应。
这相当于把回调逻辑从任务内部解耦出来,更灵活,也更容易单元测试。
- 创建
std::promise<int></int>,把promise.get_future()传给监听方,promise.set_value(42)或promise.set_exception(...)触发回调点 - 务必保证
promise对象生命周期长于调用set_value的时刻,否则程序崩溃(常见于局部promise在线程中被销毁) - 若需多处监听同一事件,用
std::shared_future替代std::future,避免 move 后失效
为什么不用 std::packaged_task?它和 promise 有什么区别
std::packaged_task 是封装了可调用对象 + 自带 promise 的组合体,适合“把函数打包扔给线程执行”,而 std::promise 更底层,适合“外部控制完成时机”。
比如网络请求收到响应包才触发回调,这时你没法提前把函数包成 packaged_task —— 因为函数执行时机不可控,必须用 promise 手动 set。
-
packaged_task构造时就要绑定函数,且只能执行一次;promise不绑定任何逻辑,纯信号机制 -
packaged_task的future也是 move-only,且一旦 task 执行完就 ready,无法像promise那样延迟 set - 两者都需注意线程安全:同一个
promise或packaged_task不能跨线程多次调用set_value或operator()
真正复杂的异步流程里,promise 的可控性更重要;简单 fire-and-forget 场景,async 最省事。别为了“看起来像回调”硬套模式,先想清楚谁该决定完成时机。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











