关于CPP——std::future异步操作

news2025/1/4 14:57:34

目录

一、std::future 简介

1.1 概念

1.2 应用场景

1.3 关联的方法

1.3.1 std::async

1.3.2 std::package

1.3.3 std::promise

二、future 用法

2.1 使用std::async关联异步任务

2.2 使用std::packaged_task

1. 获取任务结果的机制:

2. 异步任务的管理:

3. 任务与执行环境分离:

4. 线程池和异步任务组合使用:

2.3 使用std::promise


一、std::future 简介

1.1 概念

std::future是C++11标准库中的⼀个模板类,它表示⼀个异步操作的结果。当我们在多线程编程中使⽤异步任务时,std::future可以帮助我们在需要的时候获取任务的执行结果。std::future的⼀个重要特性是能够阻塞当前线程,直到异步操作完成,从⽽确保我们在获取结果时不会遇到未完成的操作。

1.2 应用场景

  1. 异步任务:当我们需要在后台执⾏⼀些耗时操作时,如网络请求或计算密集型任务等,std::future 可以用来表示这些异步任务的结果。通过将任务与主线程分离,我们可以实现任务的并行处理,从而提⾼程序的执⾏效率
  2. 并发控制:在多线程编程中,我们可能需要等待某些任务完成后才能继续执⾏其他操作。通过使⽤std::future,我们可以实现线程之间的同步,确保任务完成后再获取结果并继续执⾏后续操作
  3. 结果获取:std::future提供了⼀种安全的⽅式来获取异步任务的结果。我们可以使用std::future::get() 函数来获取任务的结果,此函数会阻塞当前线程,直到异步操作完成。这样,在调用 get() 函数时,我们可以确保已经获取到了所需的结果

1.3 关联的方法

1.3.1 std::async

std::async 是一个函数模板:它会根据用户设置的传入参数选择不同的方案,异步执行一个函数,返回一个 future 对象用于获取函数结果。

1.3.2 std::package

std::packaged_task 是一个类模板:为一个函数生成一个异步任务对象,用于在其他线程中执行。是对函数的二次封装。

1.3.3 std::promise

std::promise 是一个类模板:实例化的对象可以返回一个 future ,在其他线程中向 promise 对象设置数据,其他线程的关联 future 就可以获取数据。是对结果的封装。

二、future 用法

2.1 使用std::async关联异步任务

 std::async 是⼀种将任务与 std::future 关联的简单⽅法。它创建并运行⼀个异步任务,并返回⼀个与该任务结果关联的 std::future 对象。
默认情况下, std::async 是否启动⼀个新线程,或者在等待 future 时,任务是否同步运行都取决于你给的参数。
这个参数为 std::launch 类型,以下介绍关于 std::launch 的三种策略:

std::launch::async 策略

  • 这个策略会立即启动一个新线程来执行异步任务。任务的执行是非阻塞的,意味着你可以在等待任务完成的同时继续执行其他代码。
  • 当你调用 future.get() 时,如果任务还未完成,程序会阻塞,直到异步任务完成并返回结果。

std::launch::deferred 策略

  • 这个策略不会立即创建线程执行任务,而是将任务推迟到你调用 future.get() 时才执行。换句话说,任务的执行是懒加载的方式,只有在你需要结果时,才会真正开始执行任务。
  • 在此模式下,任务的执行是同步的,意味着在你调用 get() 时任务才会运行,且在运行结束之前程序会被阻塞。因此,在调用 get() 之前不会创建新线程,所有任务都在调用 get() 的线程中完成。

std::launch::async | std::launch::deferred 策略

  • 内部通过系统等条件⾃动选择策略


#include <iostream>
#include <future>

int Add(int num1, int num2)
{
    return num1 + num2;
}

int main()
{
    // std::launch::async策略:内部创建一个线程执行函数,函数运行结果通过future获取
    // std::launch::deferred策略:同步策略,获取结果的时候再去执行任务
    std::future<int> res = std::async(std::launch::deferred, Add, 11, 22); // 进行了一个异步非阻塞调用
    // std::future<int>::get()  用于获取异步任务的结果,如果还没有结果就会阻塞
    std::cout << res.get() << std::endl;
    return 0;
}

2.2 使用std::packaged_task

std::packaged_task就是将任务和 std::feature 绑定在⼀起的模板,是⼀种对任务的封装。
我们可以通过 std::packaged_task 对象获取任务相关联的 std::future 对象,通过调用 get_future() 方法获得。

总结下来使用 std::packaged_task 的步骤就是:

1. 使用 package_task 封装需要异步操作的方法,可以使用 shared_ptr 进一步封装方便管理生命周期

auto task = std::make_shared < std::packaged_task < 返回值 ( 传入参数 ) > > ( 函数名 ) ;

2. 使用 future 模板获取 package_task 关联的 future 对象

std::future < 返回值类型res = task -> get_future() ;

3. 创建新线程执行异步操作的函数

4. 使用 future 的 get() 方法获取函数执行的结果

res.get()

#include <iostream>
#include <future>
#include <thread>
#include <memory>

int Add(int num1, int num2)
{
    return num1 + num2;
}

int main()
{
    // 1. 封装任务
    auto task = std::make_shared<std::packaged_task<int(int, int)>>(Add);

    // 2. 获取任务包关联的future对象
    std::future<int> res = task->get_future();

    std::thread thr([task]()
                    { (*task)(11, 22); });

    // 3. 获取结果
    std::cout << res.get() << std::endl;
    thr.join();
    return 0;
}

观察了以上代码,你可能会有疑问,既然使用 future 与 package_task 也要新创建线程,那么为什么不直接创建新线程执行回调函数呢?下面会解答这个疑问:

使用 std::packaged_taskstd::future 的主要目的是为任务的结果获取机制提供更方便的方式,尤其是在任务和结果分离的场景下,而不仅仅是启动一个线程。这与直接使用 std::thread 的方式相比,有以下几个关键优势:

1. 获取任务结果的机制

  • std::packaged_task 会将一个任务(函数)与 std::future 绑定在一起,因此你可以通过 future.get() 来获取任务的执行结果。
  • 当你只使用 std::thread 时,线程完成的任务结果必须通过其他方式传递,比如共享状态、回调函数等,这些方式通常更复杂且容易出错。而 std::future 提供了一个更安全和简单的接口。

2. 异步任务的管理

  • 使用 std::future 可以轻松等待异步任务完成,通过调用 future.get() 可以阻塞当前线程,直到任务完成并返回结果。而使用 std::thread 时,没有一个简单的方法来阻塞线程并获取任务结果,必须自己处理同步机制。
  • std::future 提供了一种更标准化的机制来处理任务的执行和结果获取,这使得代码的可读性和可维护性更好。

3. 任务与执行环境分离

  • std::packaged_taskstd::future 将任务(Add 函数)与它的执行环境(线程)解耦。你可以在任何时候启动任务并获取结果,而不必担心线程的创建和管理。这为任务调度提供了更大的灵活性,比如可以通过不同的方式执行任务:立即执行、推迟执行或在线程池中执行。
  • 直接使用 std::thread 需要你手动管理线程的生命周期,比如什么时候开始、什么时候结束、如何获取结果等。

4. 线程池和异步任务组合使用

  • 在一些场景下,你可能会将任务提交到一个线程池中,而不是直接创建线程。在这种情况下,使用 std::packaged_taskstd::future 可以非常方便地处理任务的提交和结果获取,而不需要手动管理线程的创建和销毁。
  • 例如,你可以将 std::packaged_task 提交给线程池,而 future 则用来等待并获取任务的结果。

2.3 使用std::promise

在使用 promise 时,先实例化一个 promise 对象,然后实例化 future 对象接收 promise 对象关联的 future 对象,这样就实现了 promise 与 future 的绑定,然后后续通过对 promise 对象做修改, future 对象就可以获得异步操作函数的返回值  

#include <iostream>
#include <future>
#include <thread>
#include <memory>

int Add(int num1, int num2)
{
    return num1 + num2;
}

int main()
{
    //   1. 在使用的时候,就是先实例化一个指定结果的promise对象,
    std::promise<int> pro;
    //   2. 通过promise对象,获取关联的future对象
    std::future<int> res = pro.get_future();
    //   3. 在任意位置给promise设置数据,就可以通过关联的future获取到这个设置的数据了
    std::thread thr([&pro]()
                    {int sum = Add(11 , 22);pro.set_value(sum); });

    std::cout << res.get() << std::endl;
    thr.join();
    return 0;  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2112992.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

jmeter之仅一次控制器

仅一次控制器作用&#xff1a; 不管线程组设置多少次循环&#xff0c;它下面的组件都只会执行一次 Tips&#xff1a;很多情况下需要登录才能访问其他接口&#xff0c;比如&#xff1a;商品列表、添加商品到购物车、购物车列表等&#xff0c;在多场景下&#xff0c;登录只需要…

【STM32 Blue Pill编程】-ADC数据采样(轮询、中断和DMA模式)

ADC数据采样(轮询、中断和DMA模式) 文章目录 ADC数据采样(轮询、中断和DMA模式)1、硬件准备及接线2、ADC轮询模式2.1 轮询模式配置2.2 代码实现3、ADC中断模式3.1 中断模式配置3.2 代码实现4、ADC的DMA模式4.1 DMA模式配置4.2 代码实现在本文中,我们将介绍如何使用 ADC 并…

[JAVA基础知识汇总-1] 创建线程的几种方式

文章目录 1. 继承Thread类2. 实现Runnable接口3. 实现Callable接口4. 线程池 可以认为有四种方式&#xff0c;也可以认为有一种&#xff0c;因为都跟Runnable接口有关 1. 继承Thread类 代码 public class Thread1ExtendsThread extends Thread { // public Thread1(String …

思维导图与头脑风暴:你值得拥有的四大工作与学习利器

在工作与学习中&#xff0c;我们都遇到过这样的情况&#xff1a;我们需要就某一问题或项目&#xff0c;汇集多人的智慧与创意&#xff0c;这时&#xff0c;头脑风暴便成为了我们不可或缺的利器&#xff1b;而为了更好地进行头脑风暴&#xff0c;选择一款合适的在线思维导图工具…

【Qt开发】QT6.5.3安装方法(使用国内源)亲测可行!!!

目录 &#x1f315;下载在线安装包&#x1f315; 把安装包放到系统盘&#x1f315;开始安装&#x1f315;参考文章 &#x1f315;下载在线安装包 https://mirrors.nju.edu.cn/qt/official_releases/online_installers/ &#x1f315; 把安装包放到系统盘 我的系统盘是G盘&…

uniapp 全屏日历,动态无限加载

不好用请移至评论区揍我 原创代码,请勿转载,谢谢! 注:本人仅在微信小程序测试过,未在其他app/h5尝试过,按理说应该是可以的,代码没有引用任何第三方组件 日历中每个日期下方的空白部分均可自定义,写在代码中的<view class="item">我是内容</view>…

Go入门指南(The Way to Go) 完整版PDF

The Way To Go可以说是入门 Go 的经典书籍&#xff0c;这本书有内容丰富各种资料链接&#xff0c;这是截止到目前&#xff0c;大叔看到的写得最好的go 语言教材&#xff0c;非常详细.一口气读下来&#xff0c;舍不得放手&#xff0c;大叔强烈推荐你去学习 百度网盘分享

docker基于minio部署outline团队知识库

outline 介绍 Outline 是一个开源的Wiki 知识库和团队协作文档管理工具&#xff0c;美观、实时协作、功能丰富且兼容 Markdown&#xff0c;设计用于帮助团队和组织有效地创建、共享和管理文档。 Outline 具有简单易用的界面和强大的功能&#xff0c;可以替代传统的文档管理系…

winndows系统修改cmd用户中文名、系统中文用户名修改为英文用户名的方法

文章目录 一、问题描述二、修改步骤1. 准备&#xff1a;2. 注销&#xff1a;3. 修改&#xff1a;4. 继续修改&#xff1a; 一、问题描述 电脑用户显示中文名&#xff08;我这里已经修改了&#xff09; 二、修改步骤 1. 准备&#xff1a; 我们需要使用 Administrator 登录&am…

OpenGL——画一个填充的三角形

1. vertShader.glsl&#xff08;顶点着色器代码&#xff09;&#xff1a; #version 460void main (void) {if (gl_VertexID 0)gl_Position vec4 (0.25, -0.25, 0.0, 1.0);else if (gl_VertexID 1)gl_Position vec4 (-0.25, -0.25, 0.0, 1.0);elsegl_Position vec4 (0.25,…

[C/C++入门][进制原理]31、求分数序列和

题目来自于信息学奥赛 1078 分析&#xff1a; 这道题看起来比较复杂&#xff0c;实际上只需要通过两个公式&#xff0c;一次性求出分母和分子&#xff0c;然后把这个求出来的数加入到变量和中。甚至都不需要知道总共游哪些数。数组都用不上。循环就能解决。 #include <ios…

快速排序(QuickSort)-归并排序(MergeSort)[java编写]

1. 快速排序 1.1 基本概述 快速排序采用分治思想&#xff0c;即在一个无序的序列中选取一个任意的基准元素pivot&#xff0c;利用pivot 将待排序的序列分成两部分&#xff0c;前面部分元素均小于或等于基准元素&#xff0c;后面部分均大于或等于基准元素&#xff0c;然后采用…

Chapter 10 Stability and Frequency Compensation

Chapter 10 Stability and Frequency Compensation Chapter 8介绍了负反馈, 这一章介绍稳定性, 如果设计不好, 负反馈系统是要发生震荡的. 首先我们学习理解稳定判断标准和条件, 然后学习频率补偿, 介绍适用于不同运放的补偿方式, 同时介绍不同补偿对两级运放slew rate的影响…

2024全新苹果cms影视源码/仿韩剧TV主题模板源码/电影视频在线观看网站源码-自适应 (PC+WAP)

源码简介 2024年最新版的苹果CMS影视源码&#xff0c;仿制了韩剧TV的主题模板&#xff0c;让网站看起来跟韩剧TV一模一样&#xff0c;而且这个源码支持在线观看电影和视频&#xff0c;无论是在电脑&#xff08;PC&#xff09;还是手机&#xff08;WAP&#xff09;上都能完美自…

网络编程之-UDP详解

&#x1f308;个人主页&#xff1a;努力学编程’ ⛅个人推荐&#xff1a; c语言从初阶到进阶 JavaEE详解 数据结构 ⚡学好数据结构&#xff0c;刷题刻不容缓&#xff1a;点击一起刷题 &#x1f319;心灵鸡汤&#xff1a;总有人要赢&#xff0c;为什么不能是我呢 &#x1f3c0…

2024年 Biomedical Signal Processing and Control 期刊投稿经验最新分享

期刊介绍 《Biomedical Signal Processing and Control 》期刊旨在为临床医学和生物科学中信号和图像的测量和分析研究提供一个跨学科的国际论坛。重点放在处理在临床诊断&#xff0c;患者监测和管理中使用的方法和设备的实际&#xff0c;应用为主导的研究的贡献。 生物医学信…

【大模型基础】P1 N-Gram 模型

目录 N-Gram 概述N-Gram 构建过程TokenN-Gram 实例第1步 构建实验语料库第2步 把句子分成 N 个 “Gram”第3步 计算每个 Bigram 在语料库中的词频第4步 计算出现的概率第5步 生成下一个词第6步&#xff1a;输入前缀&#xff0c;生成连续文本 上述实例完整代码N-Gram 的局限性 N…

一文理解粒子滤波

0. 粒子滤波流程 之前学习记录的文档&#xff0c;这里也拿出来分享一下~ 基本原理&#xff1a;随机选取预测域的 N NN 个点&#xff0c;称为粒子。以此计算出预测值&#xff0c;并算出在测量域的概率&#xff0c;即权重&#xff0c;加权平均就是最优估计。之后按权重比例&…

揭秘帕金森症幕后元凶:是哪些因素悄悄“震颤”了生活?

在这个快节奏的时代&#xff0c;健康成为了我们最宝贵的财富之一。然而&#xff0c;有一种名为“帕金森病”的神秘疾病&#xff0c;正悄悄影响着无数人的生活&#xff0c;让他们的日常充满了“不由自主”的颤抖。今天&#xff0c;就让我们一起揭开帕金森症的神秘面纱&#xff0…

【电路笔记】-同相运算放大器

同相运算放大器 文章目录 同相运算放大器1、概述2、理想的同相运算放大器3、实际同相运算放大器3.1 闭环增益3.2 输出阻抗3.3 输入阻抗4、同相运算放大器示例4.1 缓冲电路4.2 示例5、总结1、概述 施加到运算放大器的电压信号可以提供给其同相输入端 (+) 或反相输入端 (-)。 这…