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 会是一个合适的选择。
  • 它适用于需要与线程池、事件驱动等复杂并发机制一起使用的场景。
相关推荐
君鼎4 小时前
C++设计模式——单例模式
c++·单例模式·设计模式
刚入门的大一新生6 小时前
C++初阶-string类的模拟实现与改进
开发语言·c++
小冯的编程学习之路6 小时前
【软件测试】:推荐一些接口与自动化测试学习练习网站(API测试与自动化学习全攻略)
c++·selenium·测试工具·jmeter·自动化·测试用例·postman
C++ 老炮儿的技术栈7 小时前
什么是函数重载?为什么 C 不支持函数重载,而 C++能支持函数重载?
c语言·开发语言·c++·qt·算法
猪八戒1.07 小时前
C++ 回调函数和Lambda表达式
c++
源远流长jerry8 小时前
匿名函数lambda、STL与正则表达式
c++
tan180°9 小时前
Linux进程信号处理(26)
linux·c++·vscode·后端·信号处理
一只鱼^_9 小时前
牛客练习赛138(首篇万字题解???)
数据结构·c++·算法·贪心算法·动态规划·广度优先·图搜索算法
李匠202410 小时前
C++GO语言微服务之Dockerfile && docker-compose②
c++·容器
2301_8035545210 小时前
c++和c的不同
java·c语言·c++