当前位置: 首页 > news >正文

【C++多线程】std::async和std::future

在C++中,std::asyncstd::future 是C++11引入的并发编程工具,主要用于处理异步任务和线程间通信。它们配合使用,可以简化多线程编程,尤其是需要异步执行任务并获取结果的场景。以下是对 std::asyncstd::future 的详细介绍,包括定义、用法、特性、常见场景和注意事项。


1. std::async

定义

std::async 是一个函数模板,用于启动一个异步任务。它会返回一个 std::future 对象,通过这个对象可以获取任务的结果或状态。std::async 的核心思想是将任务的执行与调用线程解耦,允许任务在后台运行(可能在新线程中,也可能是延迟执行)。

语法

template <class Fn, class... Args>
std::future<std::invoke_result_t<Fn, Args...>> async(std::launch policy, Fn&& fn, Args&&... args);
  • policy:指定任务的启动策略(见下文)。
  • fn:要执行的函数或可调用对象。
  • args:传递给 fn 的参数。
  • 返回值:一个 std::future 对象,用于获取任务结果。

启动策略(std::launch

std::async 的第一个参数是一个 std::launch 枚举值,控制任务的执行方式:

  • std::launch::async:立即在新线程中异步执行任务。
  • std::launch::deferred:延迟执行任务,直到 futureget()wait() 被调用(在调用线程中同步执行)。
  • std::launch::async | std::launch::deferred(默认):实现决定是立即异步执行还是延迟执行。

如果不指定 policy,默认是 std::launch::async | std::launch::deferred,具体行为取决于编译器和运行时环境。

示例

#include <iostream>
#include <future>

int compute() {
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时任务
    return 42;
}

int main() {
    // 使用 std::launch::async 强制异步执行
    std::future<int> fut = std::async(std::launch::async, compute);

    std::cout << "主线程继续执行...\n";
    int result = fut.get(); // 等待任务完成并获取结果
    std::cout << "结果: " << result << "\n";

    return 0;
}
输出
主线程继续执行...
结果: 42
  • compute 在一个新线程中运行,主线程无需等待即可继续执行。
  • fut.get() 阻塞直到结果可用。

2. std::future

定义

std::future 是一个类模板,表示一个异步操作的未来结果。它通常与 std::asyncstd::promisestd::packaged_task 配合使用,提供了一种机制来获取异步任务的结果或状态。

主要方法

  • get()
    获取异步任务的结果。如果结果未准备好,会阻塞当前线程;如果任务抛出异常,则重新抛出。

    • 返回类型:任务返回值的类型。
    • 注意:只能调用一次,第二次调用是未定义行为。
  • wait()
    等待任务完成,但不返回结果。不会抛出异常。

  • wait_for(duration)
    等待指定时间,返回 std::future_status

    • std::future_status::ready:任务已完成。
    • std::future_status::timeout:超时。
    • std::future_status::deferred:任务被延迟执行。
  • wait_until(time_point)
    类似于 wait_for,但指定一个绝对时间点。

  • valid()
    检查 future 是否有效(即是否关联了一个异步任务)。

特性

  • 不可复制std::future 只能移动(std::move),确保结果的所有权唯一。
  • 一次性get() 调用后,future 变为无效状态。

3. std::asyncstd::future 的配合

std::async 返回的 std::future 是获取异步任务结果的关键。以下是一个更详细的例子:

#include <iostream>
#include <future>
#include <chrono>

int slow_add(int a, int b) {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟慢速计算
    return a + b;
}

int main() {
    // 异步执行 slow_add
    std::future<int> fut = std::async(std::launch::async, slow_add, 3, 4);

    std::cout << "等待结果...\n";

    // 检查状态
    auto status = fut.wait_for(std::chrono::seconds(1));
    if (status == std::future_status::timeout) {
        std::cout << "任务还未完成,继续等待...\n";
        fut.wait(); // 继续等待直到完成
    }

    int result = fut.get();
    std::cout << "结果: " << result << "\n";

    return 0;
}
输出
等待结果...
任务还未完成,继续等待...
结果: 7
  • slow_add 在新线程中运行,耗时 2 秒。
  • 主线程用 wait_for 检查 1 秒后任务未完成,然后用 wait 等待剩余时间,最后通过 get 获取结果。

4. 常见使用场景

(1)异步计算

当需要执行耗时任务(如计算、网络请求)时,使用 std::async 将任务放到后台,主线程可以继续其他工作。

std::future<double> fut = std::async(std::launch::async, [] {
    // 模拟复杂计算
    return 3.14 * 2;
});
double result = fut.get();

(2)并行任务

运行多个独立任务并收集结果:

std::future<int> fut1 = std::async(std::launch::async, slow_add, 1, 2);
std::future<int> fut2 = std::async(std::launch::async, slow_add, 3, 4);
int sum = fut1.get() + fut2.get(); // 并行计算,结果为 10

(3)异常处理

异步任务中的异常会通过 future 传递:

std::future<int> fut = std::async(std::launch::async, [] {
    throw std::runtime_error("错误发生");
    return 0;
});
try {
    fut.get(); // 抛出异常
} catch (const std::exception& e) {
    std::cout << "捕获异常: " << e.what() << "\n";
}

5. std::promise 的关系

std::async 是更高层次的抽象,内部可能使用 std::promise 和线程来实现。相比之下,std::promise 提供了更底层的控制:

  • std::async:自动管理线程和结果传递,适合简单场景。
  • std::promise:手动设置结果或异常,适合需要显式控制的复杂线程通信。

例如,std::async 的效果可以用 std::promise 模拟:

std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t([](std::promise<int> p) { p.set_value(42); }, std::move(prom));
int result = fut.get();
t.join();

6. 注意事项

(1)线程管理

  • 如果用 std::launch::async,会创建新线程;线程资源有限,滥用可能导致性能问题。
  • 如果 future 未被使用(未调用 getwait),析构时会阻塞,直到任务完成(std::launch::async 的情况下)。

(2)延迟执行

默认策略下,任务可能不会立即执行,而是等到 get()wait() 调用时才运行。强制异步需明确指定 std::launch::async

(3)异常安全

任务抛出的异常会被 future 保存,调用 get() 时抛出,需妥善处理。

(4)性能开销

创建线程和上下文切换有开销,小任务可能不适合用 std::async,考虑线程池替代。


7. 总结

  • std::async
    • 用于启动异步任务,提供简单的高级接口。
    • 通过策略控制执行方式(异步或延迟)。
  • std::future
    • 表示异步任务的结果,支持等待、获取值或异常。
    • asyncpromise 等配合使用。

它们特别适合需要异步执行并获取结果的场景,相比手动管理线程和锁,代码更简洁、安全。如果需要更细粒度的控制,可以结合 std::promise 或线程池使用。

相关文章:

  • 《从零构建企业级容器镜像生态:Harbor与Registry双星架构实战手记》
  • 【redis】布隆过滤器的Java实现
  • DR和BDR的选举规则
  • 蓝桥-找到最多的数-oj3227
  • Android Telephony 四大服务和数据网络控制面数据面介绍
  • Denoising Diffusion Probabilistic Models
  • HTML单页在线自适应拟态影院源码
  • java2025年常见设计模式面试题
  • 我的三维引擎独立开发之路:坚持与迷茫
  • 通领科技冲刺北交所
  • 计算机网络:计算机网络的概念
  • 【JavaScript】09-构造函数+数据常用函数
  • Node.js原型链污染
  • 大数据_数仓建模_八股
  • 开源项目介绍:Native-LLM-for-Android
  • mitt 依赖库详解
  • 安徽省考计算机专业科目2025(持续更新)
  • SpringCloud—概述—01
  • 人工智能之数学基础:正交矩阵
  • React封装通用Form组件,类型转换、命名控件样式隔离、支持表单验证、位置自定义、自定义布局、提示信息info等功能。未采用二次封装调整灵活,包含使用文档
  • 网站制作web678/企业宣传册
  • python怎么做网站/宁波企业网站seo
  • 网站建设的7种流程图/长尾关键词什么意思
  • 网站建设服务方案ppt模板下载/搭建一个网站的流程
  • 做爰全过程免费的视频网站爱/今日新闻最新消息大事
  • 微信如何做自己的网站/优化设计五年级下册语文答案