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 会是一个合适的选择。
  • 它适用于需要与线程池、事件驱动等复杂并发机制一起使用的场景。
相关推荐
Larry_Yanan21 小时前
QML学习笔记(四十二)QML的MessageDialog
c++·笔记·qt·学习·ui
R-G-B21 小时前
【35】MFC入门到精通——MFC运行 不显示对话框 MFC界面不显示
c++·mfc·mfc运行 不显界面·mfc界面不显示
Madison-No71 天前
【C++】探秘vector的底层实现
java·c++·算法
晚风残1 天前
【C++ Primer】第十二章:动态内存管理
开发语言·c++·c++ primer
liu****1 天前
8.list的模拟实现
linux·数据结构·c++·算法·list
保持低旋律节奏1 天前
C++ stack、queue栈和队列的使用——附加算法题
c++
初圣魔门首席弟子1 天前
【C++ 学习】单词统计器:从 “代码乱炖” 到 “清晰可品” 的复习笔记
开发语言·c++
十五年专注C++开发1 天前
CFF Explorer: 一款Windows PE 文件分析的好工具
c++·windows·microsoft
郝学胜-神的一滴1 天前
计算机图形学中的光照模型:从基础到现代技术
开发语言·c++·程序人生·图形渲染
深耕AI1 天前
MFC + OpenCV 图像预览显示不全中断问题解决:GDI行填充详解
c++·opencv·mfc