wait()仅阻塞至任务完成而不取值,get()则等待并返回结果且只能调用一次;重复调用或先wait()后get()会抛出std::future_error;get()还会重抛任务中异常。

wait() 和 get() 的区别必须分清
wait() 只阻塞到任务完成,不取结果;get() 既等待又返回值,且只能调用一次。多次调用 get() 会抛出 std::future_error(错误码为 std::future_errc::no_state 或 std::future_errc::future_already_retrieved)。
- 如果只需要同步等待、不关心返回值,用
wait()更轻量 - 如果要取值,直接用
get(),别先wait()再get()—— 这样多此一举,还可能因状态已转移导致异常 -
get()在任务异常结束时会重新抛出原始异常(比如std::runtime_error),需用try/catch包裹
auto f = std::async([]{ return 42; });
// ✅ 正确:一步到位
int x = f.get(); // 等待 + 取值
<p>// ❌ 错误:重复取值
f.wait();
int y = f.get(); // 抛出 future_error</p>wait_for() 和 wait_until() 用于带超时的等待
wait_for() 接 std::chrono::duration(如 std::chrono::seconds(2)),wait_until() 接绝对时间点(如 std::chrono::steady_clock::now() + 2s)。两者都返回 std::future_status 枚举,常见值是 ready、timeout、deferred。
- 返回
timeout时,get()仍可安全调用(只要 future 仍有有效状态),它会继续阻塞直到就绪 - 不要假设超时后 future 就“失效”了 —— 它只是还没完成,状态依然合法
- 若用
std::launch::deferred启动,wait_for()会立即返回deferred,且get()会同步执行函数体(不新开线程)
auto f = std::async(std::launch::async, []{
std::this_thread::sleep_for(3s);
return "done";
});
if (f.wait_for(1s) == std::future_status::timeout) {
std::cout << "still running...\n";
}
std::string s = f.get(); // 这里才真正等完并取值
shared_future 支持多次 get()
普通 std::future 移动后原对象失效,无法共享;而 std::shared_future 可拷贝,多个副本都能独立调用 get() 或 wait()。
- 从
std::future创建:调用.share()成员函数,原 future 失效(变成空状态) - 常用于需要多个线程分别等待/取值的场景,比如一个异步计算结果被 UI 线程和日志线程同时消费
- 注意:所有
shared_future副本共享同一内部状态,任一副本调用get()都会触发等待(如果未就绪),但不会互相干扰取值逻辑
auto f = std::async([]{ return 123; });
std::shared_future<int> sf = f.share(); // f now invalid
<p>auto t1 = std::thread([&sf]{ std::cout << sf.get() << "\n"; });
auto t2 = std::thread([&sf]{ std::cout << sf.get() << "\n"; });
t1.join(); t2.join(); // 两个线程都拿到 123</p>future 对象销毁时未等待的后果
如果 std::future(非 deferred 模式)在析构前没调用 wait() 或 get(),且对应异步任务仍在运行,程序会调用 std::terminate() —— 这是标准强制行为,不是 bug。
立即学习“C++免费学习笔记(深入)”;
- 原因:避免“遗弃”后台任务导致资源泄漏或逻辑断裂
- 最简单的规避方式是确保 future 生命周期覆盖任务完成,例如把 future 存在栈上、并在作用域末尾显式
get() - 若确实不需要结果,可用
std::async(std::launch::async, ...).wait();忽略返回值,但注意这会阻塞当前线程
{
auto f = std::async([]{ /* long task */ });
// 忘记 wait/get → 析构时 terminate!
} // f 被销毁,但任务可能还在跑
future 的核心约束其实就两条:状态只能读取一次,销毁前必须消费掉。其余所有问题,基本都是在这两条边界上踩出来的。


















