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