std::async 和 std::packaged_task

0、背景

在现代 C++ 中,std::async 和 std::packaged_task 是两个非常重要的工具,能够帮助我们更好地处理并发和异步操作。它们分别代表了异步执行任务的两种不同的方式,但都可以有效地将任务的执行从主线程或调用线程中分离出来,以提高程序的并发性和响应能力。

1、std::async

1.1、std::async的基本语法

std::async 是 C++11 引入的一个函数模板,它用于启动一个异步操作,并返回一个 std::future 对象,这个 future 对象可以用来获取异步操作的结果。std::async 会根据指定的策略异步地执行一个函数或可调用对象。函数签名如下:

cpp 复制代码
std::future<T> std::async(std::launch policy, Func&& f, Args&&... args);
  • policy: 启动策略,决定任务如何执行。它有两个可能的值:
    std::launch::async :表示任务将异步执行(即在一个新的线程中执行);
    std::launch::deferred:表示任务将延迟执行,直到调用 future.get() 或 future.wait() 来获取结果时才开始执行。
  • f: 一个可调用对象(例如函数指针、lambda 表达式或函数对象)。
  • args: 传递给可调用对象的参数
    std::async 返回一个 std::future 类型的对象,T 是任务的返回类型。可以通过 future.get() 来获取任务的结果。需要注意的是,如果任务还没有完成,调用 get() 会阻塞直到任务完成。

1.2、使用用例

cpp 复制代码
#include <iostream>
#include <future>
#include <thread>

int async_task(int x, int y) {
    std::this_thread::sleep_for(std::chrono::seconds(2));  // 模拟耗时任务
    return x + y;
}

int main() {
    // 启动异步任务
    std::future<int> result = std::async(std::launch::async, async_task, 5, 3);

    // 在等待异步结果的同时,可以做其他工作
    std::cout << "Doing other work in main thread..." << std::endl;

    // 获取异步任务结果,get() 会阻塞直到任务完成
    int sum = result.get();
    std::cout << "Async task result: " << sum << std::endl;

    return 0;
}

在上面的例子中,std::async 启动了一个异步任务,并返回一个 std::future 对象,主线程可以在不阻塞的情况下继续做其他工作,直到调用 result.get() 时才会等待异步任务的完成并获取结果。

1.3、启动策略

std::launch 是一个枚举,决定任务的执行方式。

  • std::launch::async:表示任务应该在独立的线程中异步执行。也就是说,当调用 std::async(std::launch::async, func) 时,func 会在一个新的线程中执行
  • std::launch::deferred:表示任务会被延迟到调用 future.get() 或 future.wait() 时才执行。即任务不会在调用 std::async 时立即启动,而是在获取结果时才启动。

2、std::packaged_task

2.1、std::packaged_task的基本语法

std::packaged_task 是 C++11 中的另一项异步操作工具,它封装了一个可调用对象,并提供一个 std::future 来获取异步操作的结果。与 std::async 不同,std::packaged_task 并不自动启动任务,调用者需要手动执行该任务。基本语法如下:

cpp 复制代码
template <typename F>
class std::packaged_task;
  • F: 一个可调用对象的类型(例如函数指针、lambda 表达式等)。
  • std::packaged_task 封装了一个任务,并且提供了 get_future() 方法来获取对应的 std::future 对象。

2.2、使用例子

cpp 复制代码
#include <iostream>
#include <future>
#include <thread>

int task(int x, int y) {
    std::this_thread::sleep_for(std::chrono::seconds(2));  // 模拟耗时任务
    return x + y;
}

int main() {
    // 创建 packaged_task 对象
    std::packaged_task<int(int, int)> task_obj(task);
    
    // 获取与 packaged_task 关联的 future 对象
    std::future<int> result = task_obj.get_future();

    // 启动新线程来执行任务
    std::thread t(std::move(task_obj), 5, 3);

    // 在等待异步结果的同时,可以做其他工作
    std::cout << "Doing other work in main thread..." << std::endl;

    // 获取异步任务结果,get() 会阻塞直到任务完成
    int sum = result.get();
    std::cout << "Task result: " << sum << std::endl;

    // 等待线程结束
    t.join();

    return 0;
}

我们使用 std::packaged_task 封装了一个任务 task,然后调用 get_future() 获取一个 std::future 对象。任务并不会自动执行,因此我们需要显式地创建一个线程来执行这个任务。最后,我们通过 result.get() 获取结果,get() 会阻塞直到任务完成。

2.3、启动策略

std::async 自动处理线程的创建和管理,而 std::packaged_task 则要求手动创建线程并执行任务 。std::async 可以选择任务延迟执行(通过 std::launch::deferred),而 std::packaged_task 始终会在调用时被启动

3、std::async 和 std::packaged_task 对比

特性 std::async std::packaged_task
任务启动时机 自动启动,可以选择异步执行或延迟执行 任务需要手动启动(通过线程或其他方式)
线程管理 自动管理线程 需要显式创建线程来执行任务
返回结果 返回一个 std::future 对象,直接可以获取结果 返回一个 std::future 对象,需要通过线程来执行任务
使用场景 简单的异步任务,适用于无需控制线程生命周期的场景 需要显式控制任务执行时机和线程的场景
启动策略 std::launch::async 或 std::launch::deferred 不支持延迟执行,只能同步启动

4、使用场景

4.1、std::async 的使用场景

  • 当你只关心任务的异步执行,而不需要显式控制线程时,std::async 是一个非常方便的选择。
  • 它适用于简单的并发任务,且希望任务自动执行,不需要管理线程的生命周期。

4.2、std::packaged_task 的使用场景

  • 当你需要手动控制任务的执行时,例如当你希望任务在某个特定时刻或者通过某种特定的方式(如通过线程池)来启动时,std::packaged_task 会是一个合适的选择。
  • 它适用于需要与线程池、事件驱动等复杂并发机制一起使用的场景。
相关推荐
2601_9561219710 小时前
二分算法(知识点+题目)
c++·算法
蒙奇·D·路飞-12 小时前
OpenClaw:开源的《合金弹头》精神续作与终极本地部署指南
c++·开源·mfc
小星星闪亮登场14 小时前
2026萌新联赛第三场-- (郑州轻工业大学)
数据结构·c++·经验分享·算法·贪心算法·排序算法·深度优先
梓䈑14 小时前
【用 Vibe Coding 实现的 C++17 在线判题系统】前端开发 + Web 自动化测试
前端·c++·ai编程
冻柠檬飞冰走茶14 小时前
PTA基础编程题目集 7-8超速判断(C++语言实现)
开发语言·数据结构·c++·算法
TunerT_TQ15 小时前
Valhalla 静态工程审阅 #018|MMCV 源码证据驱动评测【大厂开源基础设施特辑】
c++·开源·mfc·#计算机视觉·#商汤科技·#openmmlab·#静态源码审计
别动我齐刘海16 小时前
Day6 unitree_G1人形机器人GMR—— MotionInput
c语言·c++·人工智能·学习·机器学习·机器人·github
Henry Zhu12316 小时前
C++17 `weak_from_this` 详解:让对象安全地观察自己
c++
June`19 小时前
warp shuffle指令
c++·人工智能·算法·cuda
乱七八糟的屋子20 小时前
【C++数值计算】NumCpp超详细入门教程(纯正Numpy语法、零学习成本、C++首选科学计算库、性能压测对比)
c++·数值计算·numpy·高性能计算·科学计算·ndarray