std::async默认不真正异步,仅延迟求值;需显式指定std::launch::async才启用新线程,且future析构会阻塞等待,get()后失效,应避免误用。

async 会真的异步执行吗?
不会,std::async 默认行为是“延迟求值”(std::launch::deferred),也就是调用时并不启动新线程,而是在你调用 get() 或 wait() 时才同步执行——这根本不是异步。
必须显式指定 std::launch::async 才能真正启用新线程:
auto fut = std::async(std::launch::async, []{ return 42; });
- 漏写
std::launch::async是最常见错误,尤其在调试时发现任务卡主线程 - 不加参数或只传函数对象,等价于
std::launch::deferred | std::launch::async,系统可自由选择策略,行为不可控 - 某些平台(如 MinGW)对
std::launch::async支持不完整,可能退化为 deferred
future.get() 调用一次就失效
std::future 是一次性消费对象:调用 get() 后,其内部状态变为“已获取”,再次调用会抛出 std::future_error(错误码为 std::future_errc::no_state)。
典型错误场景:
立即学习“C++免费学习笔记(深入)”;
auto f = std::async(std::launch::async, []{ return 123; });
int a = f.get(); // ✅ 正常
int b = f.get(); // ❌ 抛异常:std::future_error
- 不能把同一个
future保存下来反复取结果 - 若需多次访问结果,应把
get()结果存入变量,或改用std::shared_future -
shared_future可拷贝,且所有副本共享同一状态,适合多处等待或读取
async 对象生命周期管理很关键
std::async 返回的 future 对象,其析构会**阻塞等待任务结束**——哪怕你没调用 get()。
这意味着:
{
auto f = std::async(std::launch::async, []{
std::this_thread::sleep_for(2s);
return 99;
});
// f 还没 get(),但作用域结束 → 析构 → 主线程卡住 2 秒!
}
- 这是单线程异步逻辑中最隐蔽的“伪异步”陷阱
- 若任务耗时长且你不关心结果,应主动调用
wait()或get()并捕获异常,或改用std::thread+ 分离(但要自己管资源) - 更安全的做法是立刻 move 出去,或用智能指针托管,避免意外析构
替代方案:不用 async 也能做单线程异步
所谓“单线程异步”,本质是**不阻塞当前线程、延后执行、支持回调或轮询**。C++11 的 async/future 天然依赖线程,不适合真·单线程调度。
更贴合的实现方式包括:
- 用
std::function+ 队列 + 主循环(如游戏/嵌入式常见的 job queue) - 基于
std::chrono和poll/select实现定时任务(无第三方库时) - 借助
boost::asio::io_context(即使只 run_one 线程)提供统一异步原语 - 现代 C++20 可用
std::jthread+std::stop_token更可控地管理任务生命周期
async 不是万能胶,它解决的是“开个线程跑任务并拿结果”,而不是“异步编程模型”。混淆这两者,容易让代码既难测又难维护。









