C++11 异步操作 std::future类

news2024/10/4 3:39:54

阅读导航

  • 引言
  • 一、异步的概念
  • 二、应用场景
    • 1. 异步任务处理
    • 2. 并发控制
    • 3. 结果获取
  • 三、使用示例
    • 1. 使用std::async关联异步任务
      • 💻示例代码
      • 说明
    • 2. 使用std::packaged_task和std::future配合
      • (1)定义std::packaged_task
      • (2)获取std::future对象
      • (3)启动异步任务
      • (4)等待异步任务完成并获取结果
    • 3. 使用std::promise和std::future配合
      • (1)创建std::promise对象
      • (2)获取std::future对象
      • (3)传递std::future对象
      • (4)在产生结果的线程中设置结果
      • (5)在消费结果的线程中获取结果
      • 📦示例代码

引言

C++11的推出,为C++编程语言带来了革命性的变化,其中std::future类作为异步编程的核心工具,让并发和异步任务的管理变得更加简洁和高效。本文将简要介绍std::future类的基本概念和用法,并通过示例展示其在实际编程中的应用,帮助您更好地理解和利用这一C++11的新特性。

一、异步的概念

异步编程是一种编程范式,它允许程序在等待某个长时间运行的操作(如文件读写、网络通信或复杂计算)完成时,不会阻塞或挂起执行线程,而是可以继续执行其他任务。这种非阻塞的执行方式可以显著提高程序的响应性和吞吐量。

在C++中,异步编程的概念通过C++11标准引入的一系列新特性得到了极大的支持和简化,其中std::future类扮演了关键角色。std::future是一个模板类,用于表示异步操作的结果。它提供了一种机制,允许程序在将来的某个时刻访问该结果,而无需在异步操作完成之前阻塞执行线程。

🔴官方文档

二、应用场景

1. 异步任务处理

在处理需要较长时间完成的任务,如网络请求、大规模数据处理或复杂计算时,std::future 提供了一种机制来代表这些异步任务的结果。通过将这些耗时的操作从主线程中分离出来,在后台执行,我们可以让主线程继续处理其他任务,从而实现任务的并行处理。这不仅提高了程序的响应速度,还优化了整体执行效率。

2. 并发控制

在多线程编程环境中,经常需要确保某些操作在另一些操作完成之后才能执行,以维护程序的状态一致性和正确性。std::future 允许我们在多线程之间实现同步控制。通过等待std::future对象代表的异步任务完成,我们可以确保在继续执行依赖于该任务结果的操作之前,该任务已经被成功完成。这种机制有助于简化并发控制逻辑,减少错误和竞态条件的发生。

3. 结果获取

std::future 提供了一种安全且便捷的方式来获取异步任务的结果。通过调用std::future::get()成员函数,我们可以尝试检索异步操作的结果。然而,需要注意的是,如果异步操作尚未完成,调用get()函数将会阻塞当前线程,直到异步操作完成并返回结果。这种方式确保了我们在继续处理结果之前,确实已经获得了所需的数据,从而避免了潜在的数据竞争和错误。因此,std::future提供了一种可靠的机制来同步访问异步操作的结果。

三、使用示例

1. 使用std::async关联异步任务

在C++中,std::async<future>库中的一个功能强大的工具,它允许你以异步方式启动一个任务,并且这个任务可以立即返回一个std::future对象,通过这个对象你可以在未来某个时刻获取到任务的结果。使用std::async可以很方便地实现并行计算或提高程序的响应性。

💻示例代码

假设我们有两个函数,分别用于执行一些耗时的计算:

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

// 第一个耗时任务
int task1() {
    std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
    return 42; // 假设的返回值
}

// 第二个耗时任务
int task2() {
    std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟耗时操作
    return 24; // 假设的返回值
}

int main() {
    // 启动两个异步任务
    auto future1 = std::async(std::launch::async, task1);
    auto future2 = std::async(std::launch::async, task2);

    // 等待并获取两个异步任务的结果
    int result1 = future1.get();
    int result2 = future2.get();

    // 输出结果
    std::cout << "Task 1 result: " << result1 << std::endl;
    std::cout << "Task 2 result: " << result2 << std::endl;

    return 0;
}

说明

  1. 启动异步任务:使用std::async时,你需要指定任务的启动策略(std::launch::asyncstd::launch::deferred或它们的组合)和要异步执行的函数。在这个例子中,我们使用了std::launch::async来确保任务在新的线程中立即开始执行。

  2. 获取任务结果std::async返回一个std::future对象,这个对象代表了异步操作的结果。你可以通过调用future.get()来等待异步操作完成并获取其结果。注意,get()会阻塞调用它的线程,直到异步操作完成。

  3. 并发执行:在这个例子中,task1task2会并发执行,因为我们在主线程中几乎同时启动了它们。它们的执行顺序和完成时间取决于操作系统的调度。

在C++中,std::packaged_taskstd::future是紧密相关的,它们通常结合使用以实现异步编程和结果传递。std::packaged_task是一个可调用的对象,它封装了一个可以异步执行的函数、lambda表达式、绑定表达式或其他可调用对象,并将该函数的执行结果存储在与std::future相关联的共享状态中。

2. 使用std::packaged_task和std::future配合

(1)定义std::packaged_task

首先,需要定义一个std::packaged_task对象,并为其提供一个返回特定类型结果的函数或可调用对象。这个函数的返回类型将与std::future的类型相关联。

#include <future>
#include <iostream>

int compute_value(int x) {
    // 假设这是一个耗时的计算
    return x * x;
}

int main() {
    std::packaged_task<int(int)> task(compute_value);
    // ...
}

(2)获取std::future对象

通过调用std::packaged_taskget_future()成员函数来获取一个std::future对象。这个future对象将用于稍后检索异步操作的结果。

    std::future<int> result = task.get_future();

(3)启动异步任务

std::packaged_task对象可以作为函数对象被调用,但通常不会直接在原线程中这样做,而是将它绑定到一个线程(例如,使用std::thread)或某个异步执行机制(如线程池)上,以异步方式执行。

    std::thread worker(std::move(task), 42); // 传递任务和一个参数
    // ...
}

注意:在将std::packaged_task传递给线程之前,必须先获取std::future对象,因为一旦std::packaged_task被移动到另一个线程,你就不能再访问原始对象来获取std::future了。

(4)等待异步任务完成并获取结果

在主线程中,你可以通过调用std::futureget()方法来等待异步任务完成并获取结果。调用get()会阻塞当前线程,直到结果可用。

    worker.join(); // 等待线程完成
    std::cout << "The result is " << result.get() << std::endl;

🚨🚨注意std::future::get()只能被调用一次,因为结果一旦被取出就无法再次访问

3. 使用std::promise和std::future配合

在C++中,std::promisestd::future是紧密相关的,它们用于在不同线程之间传递值或异常。std::promise对象允许你在一个线程中设置结果值或异常,而std::future对象则用于在另一个线程中获取这些值或异常。这种机制特别适用于异步编程,其中任务的执行和结果的使用可能发生在不同的线程中。

(1)创建std::promise对象

首先,在产生结果的线程中创建一个std::promise对象。这个对象将用于设置结果值或异常。

(2)获取std::future对象

通过调用std::promise对象的get_future()成员函数来获取一个std::future对象。这个future对象将用于在另一个线程中获取结果。

(3)传递std::future对象

std::future对象传递给需要结果的线程。这通常通过函数参数、全局变量、共享数据结构或其他线程间通信机制来完成。

(4)在产生结果的线程中设置结果

在产生结果的线程中,使用std::promise对象的set_value()成员函数来设置结果值,或者使用set_exception()来设置异常(如果需要的话)。一旦设置了值或异常,与之关联的future对象就会变为“就绪”状态。

(5)在消费结果的线程中获取结果

在消费结果的线程中,使用std::future对象的get()成员函数来获取结果。如果结果已经就绪,get()将立即返回结果值。如果结果尚未就绪,get()将阻塞当前线程,直到结果变为就绪状态。

📦示例代码

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

void compute_and_set_result(std::promise<int> prom) {
    // 假设这是一个耗时的计算
    int result = 42; // 假设的计算结果
    
    // 设置结果值
    prom.set_value(result);
}

int main() {
    // 创建一个promise对象
    std::promise<int> prom;
    
    // 获取与promise关联的future对象
    std::future<int> fut = prom.get_future();
    
    // 启动一个线程来执行耗时的计算并设置结果
    std::thread worker(compute_and_set_result, std::move(prom));
    
    // 等待结果并打印
    std::cout << "The result is " << fut.get() << std::endl;
    
    // 确保线程完成
    worker.join();
    
    return 0;
}

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

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

相关文章

游戏修改器Cheat Engine CE v7.5修改版下载安装详细方法

Cheat Engine是一个专注于游戏的修改器。它可以用来扫描游戏中的内存&#xff0c;并允许修改它们。它还附带了调试器、反汇编器、汇编器、变速器、作弊器生成、Direct3D操作工具、系统检查工具等。 具体安装方法如下&#xff1a; 地址&#xff1a;Cheat Engine 7.5.zip 解压文件…

Prompt 初级版:构建高效对话的基础指南

Prompt 初级版&#xff1a;构建高效对话的基础指南 文章目录 Prompt 初级版&#xff1a;构建高效对话的基础指南一 “标准”提示二 角色提示三 多范例提示四 组合提示五 规范化提示 本文介绍了提示词的基础概念与不同类型&#xff0c;帮助用户更好地理解如何在对话中构建有效的…

Java 计算器项目

更多有趣请关注公众号 计算器项目 代码仓库&#xff1a;https://gitee.com/wengxiulin/vs_code 项目图片 项目简介 这是一个用 Java 编写的简单计算器应用程序&#xff0c;具有基本的数学运算功能。该计算器支持加、减、乘、除等运算&#xff0c;并提供用户友好的图形界面…

CSP-J模拟赛三补题报告

前言 挂了110pts( ⇑ \Uparrow ⇑ \hspace{14em} 有史以来最大傻逼 T1&#xff1a; 100 p t s \color{green}100pts 100pts T2: 100 p t s → 80 p t s \color{green}100pts\color{yellow}\rightarrow\color{red}80pts 100pts→80pts T3: 100 p t s → 10 p t s \color{gre…

java 的三种IO模型(BIO、NIO、AIO)

java 的三种IO模型&#xff08;BIO、NIO、AIO&#xff09; 一、BIO 阻塞式 IO&#xff08;Blocking IO&#xff09;1.1、BIO 工作机制1.2、BIO 实现单发单收1.3、BIO 实现多发多收1.4、BIO 实现客户端服务端多对一1.5、BIO 模式下的端口转发思想 二、NIO 同步非阻塞式 IO&#…

【Godot4.3】模拟平面图形绕轴或点在空间旋转

概述 平面图形&#xff0c;除了常规的线性变换&#xff1a;平移、缩放、旋转、斜切之外。还可以模仿在三维空间旋转、透视等等。 矩形绕纵对称轴旋转实点的轨迹 绕对称旋转是个特殊情况&#xff0c;轨迹是圆也是为了便于理解。更实际的情况应该是椭圆。非对称轴旋转的情况轨…

Java 之深入理解 String、StringBuilder、StringBuffer

前言 由于发现 String、StringBuilder、StringBuffer 面试的时候会经常问到&#xff0c;这里就顺便总结一下&#xff1a;本文重点会以这三个字符串类的性能、线程安全、存储结构这三个方面进行分析 ✨上期回顾&#xff1a;Java 哈希表 ✨目录 前言 String 介绍 String 的不可变…

2024/10/3 408数据结构大题打卡

最短路径复习&#xff1a; bfs&#xff1a;只能解决无权图

【LeetCode每日一题】——17.电话号码的字母组合

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 回溯 二【题目难度】 中等 三【题目编号】 17.电话号码的字母组合 四【题目描述】 给定一个…

redis 5的安装及启动(window)

最近看大模型的时候发现入手redis的同学没有练手的&#xff0c;而且大部分redis的文章要钱才能看&#xff0c;在这里我把路径和环境配置&#xff0c;启动给大家说一下 下载 redis5的获取链接在下面&#xff08;为什么是redis5&#xff0c;因为上个模型用的就是redis5&#xff…

pipe函数的例子

代码&#xff1a; #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/types.h> int main(void) {int result -1;int fd[2],nbytes;pid_t pid;char string[80]"ni hao, pipe!";char readbuff…

linux信号 | 学习信号四步走 | 一篇文章教你理解信号如何保存

前言&#xff1a; 本节内容是信号的保存。 学习信号&#xff0c; 我们首先了解了信号的概念&#xff0c; 然后学习了信号的产生方式。 现在就开始讲解信号在时间窗口内是如何保存在进程内部的。 ps&#xff1a;本节内容需要了解信号的概念&#xff0c; 希望友友们了解一些信号…

实用技能分享!推荐最适合论文写作的5款ai工具

在当今学术研究和教育领域&#xff0c;AI工具的应用已经变得越来越普遍。这些工具不仅能够提高写作效率&#xff0c;还能帮助生成高质量的文稿。对于教师而言&#xff0c;选择合适的AI工具可以显著提升论文写作的效率和质量。本文将重点推荐五款最适合教师论文写作的AI工具&…

Linux聊天集群开发之环境准备

一.windows下远程操作Linux 第一步&#xff1a;在Linux终端下配置openssh&#xff0c;输入netstate -tanp,查看ssh服务是否启动&#xff0c;默认端口22.。 注&#xff1a;如果openssh服务&#xff0c;则需下载。输入命令ps -e|grep ssh, 查看如否配有&#xff0c; ssh-agent …

【重学 MySQL】四十六、创建表的方式

【重学 MySQL】四十六、创建表的方式 使用CREATE TABLE语句创建表使用CREATE TABLE LIKE语句创建表使用CREATE TABLE AS SELECT语句创建表使用CREATE TABLE SELECT语句创建表并从另一个表中选取数据&#xff08;与CREATE TABLE AS SELECT类似&#xff09;使用CREATE TEMPORARY …

【重学 MySQL】五十四、整型数据类型

【重学 MySQL】五十四、整型数据类型 整型类型TINYINTSMALLINTMEDIUMINTINT&#xff08;或INTEGER&#xff09;BIGINT 可选属性UNSIGNEDZEROFILL显示宽度&#xff08;M&#xff09;AUTO_INCREMENT注意事项 适合场景TINYINTSMALLINTMEDIUMINTINT&#xff08;或INTEGER&#xff0…

Python 从入门到实战33(使用MySQL)

我们的目标是&#xff1a;通过这一套资料学习下来&#xff0c;通过熟练掌握python基础&#xff0c;然后结合经典实例、实践相结合&#xff0c;使我们完全掌握python&#xff0c;并做到独立完成项目开发的能力。 上篇文章我们讨论了数据库编程接口操作的相关知识。今天我们将学习…

SLF4J(W): Class path contains multiple SLF4J providers.

问题背景 最近在给某AI项目集成阿里的通义千问SDK&#xff0c;发现竟然有个奇怪的报错&#xff0c;仔细一看发现&#xff0c;我类上用的lombok的Slf4j注释&#xff0c;阿里用的是org.slf4j.simple.SimpleServiceProvider&#xff0c;但是lombok用的是LogbackServiceProvider&a…

关于Vben Admin多标签页面缓存不生效的问题

情况说明 笔者在接手一个基于Vben Admin框架改造的vue3后台管理项目&#xff0c;客户要求在切换头部Tab页面时&#xff0c;不要刷新清空已经填写的表单页面或者表格。 然而&#xff0c;笔者根据Vben Admin的官方文档来配置多标签页面缓存后&#xff0c;页面每次切换后&#x…

Linux 应用层协议HTTP

文章目录 一、初始HTTP协议二、URL格式网络中怎么通过URL进行定位资源呢&#xff1f;编码和解码 三、HTTP的请求格式和响应格式HTTP的请求格式HTTP的响应格式HTTP的请求方法GET方法POST方法GET Vs PostHTTP的封装和分用文件流操作浏览器获得一个完整的网页流程 HTTP的状态码对3…