c++中如何使用std::async实现简单的并发任务_c++异步编程【实例】

12次阅读

必须显式指定 std::launch::async 才真正并发;未取值的 future 析构会触发 std::terminate;捕获局部变量需确保生命周期安全,推荐值捕获或 shared_ptr。

c++中如何使用std::async实现简单的并发任务_c++异步编程【实例】

直接用 std::async 启动一个异步任务,最简方式就是调用它并获取 std::future,但默认启动策略(std::launch::deferredstd::launch::async)不明确,容易误以为“已并发执行”,结果变成延迟调用——这是新手踩坑最多的地方。

必须显式指定 std::launch::async 才真正并发

如果不传启动策略,std::async 可能选择 deferred:函数体直到调用 get()wait() 时才执行,且在调用线程中同步运行,根本没开新线程。

  • 永远显式写 std::async(std::launch::async, ...) 来确保立即派生线程
  • std::launch::deferred | std::launch::async 是合法组合,但行为由实现决定,不可靠
  • c++20 起,std::launch::async 是唯一能保证异步执行的策略

获取返回值前必须处理异常或调用 get()

std::future 析构时若未取值,且异步任务抛出异常,则程序会调用 std::terminate——不是崩溃,是直接终止,无、无提示。

  • 务必在 future 生命周期内调用 get()(哪怕只读一次)
  • 若不关心返回值,可用 wait() 等待完成,但依然要确保异常被传播出来(get() 会重新抛出)
  • 推荐用 try-catch 包裹 get(),尤其当异步函数可能失败时

多个 std::async 任务需注意资源竞争与生命周期

每个 std::async 默认使用系统线程池(具体实现依赖标准库),但线程数不受控;同时捕获局部变量时,若异步任务生命周期超过变量作用域,就会悬垂引用。

立即学习C++免费学习笔记(深入)”;

  • 避免按值捕获大对象,优先用 std::move 或共享指针(如 std::shared_ptr
  • 不要在 Lambda 中直接捕获局部变量的引用([&]),除非能 100% 确保其存活到任务结束
  • 大量任务建议改用线程池(如 boost::asio::thread_pool 或自建),std::async 不适合高吞吐调度
int main() {     // 正确:显式 async,捕获值,安全等待     auto fut1 = std::async(std::launch::async, []{ return 42; });     auto fut2 = std::async(std::launch::async, []{          std::this_thread::sleep_for(100ms);          return 100;      });      // 必须 get(),否则析构时异常导致 terminate     try {         int a = fut1.get();  // 阻塞直到完成         int b = fut2.get();         std::cout << a + b << "n";  // 输出 142     } catch (const std::exception& e) {         std::cerr << "Async task failed: " << e.what() << "n";     } }

真正麻烦的从来不是怎么写那几行 std::async,而是谁来管理线程生命周期、异常怎么透出、结果怎么聚合、任务失败后要不要重试——这些 std::async 一概不管,得你自己兜底。

text=ZqhQzanResources