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

阅读导航

  • 引言
  • 一、异步的概念
  • 二、应用场景
    • 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.xdnf.cn/news/1553437.html

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

相关文章

Pikachu-Cross-Site Scripting-DOM型xss

DOM型xss DOM型XSS漏洞是一种特殊类型的XSS,是基于文档对象模型 Document Object Model (DOM)的一种漏洞。是一个与平台、编程语言无关的接口&#xff0c;它允许程序或脚本动态地访问和更新文档内容、结构和样式&#xff0c;处理后的结果能够成为显示页面的一部分。 dom就是一…

【Qt】控件概述 (1)

控件概述 1. QWidget核心属性1.1核心属性概述1.2 enable1.3 geometry——窗口坐标1.4 window frame的影响1.4 windowTitle——窗口标题1.5 windowIcon——窗口图标1.6 windowOpacity——透明度设置1.7 cursor——光标设置1.8 font——字体设置1.9 toolTip——鼠标悬停提示设置1…

后台管理系统脚手架

后台管理系统脚手架 介绍 在快速迭代的软件开发世界里&#xff0c;时间就是生产力&#xff0c;效率决定成败。对于构建复杂而庞大的后台系统而言&#xff0c;一个高效、可定制的后台脚手架&#xff08;Backend Scaffold&#xff09;无疑是开发者的得力助手。 脚手架 后台脚…

GO网络编程(一):基础知识

1. 网络编程的基础概念 TCP/IP 协议栈 TCP/IP 是互联网通信的核心协议栈&#xff0c;分为以下四个层次&#xff1a; 应用层&#xff08;Application Layer&#xff09;&#xff1a;为应用程序提供网络服务的协议&#xff0c;比如 HTTP、FTP、SMTP 等。传输层&#xff08;Tra…

C++中stack和queue的模拟实现

目录 1.容器适配器 1.1什么是适配器 1.2STL标准库中stack和queue的底层结构 1.3deque的简单介绍 1.3.1deque的原理介绍 1.3.2deque的优点和缺陷 1.3.3deque和vector进行排序的性能对比 1.4为什么选择deque作为stack和queue的底层默认容器 2.stack的介绍和模拟…

数据库第8章编程题2

10-1 查询选修某两门课程的学生&#xff08;MSSQL) 本题目要求编写SQL语句&#xff0c; 检索出 sc表中至少选修了’C001’与’C002’课程的学生学号。 提示&#xff1a;MSSQLServer 评测SQL语句。 表结构: 请在这里写定义表结构的SQL语句。例如&#xff1a; -- 学生选课成…

前端组件化开发

假设这个页面是vue开发的&#xff0c;如果一整个页面都是编写在一个vue文件里面&#xff0c;后期不好维护&#xff0c;会特别的庞大&#xff0c;那么如何这个时候需要进行组件化开发。组件化开发后必然会带来一个问题需要进行组件之间的通信。组要是父子组件之间通信&#xff0…

SuperMap iClient for MapLibreGL 根据SQL条件过滤显示动态图层

查阅发现iClient 有子图层控制类 LayerStatus 可实现&#xff1a;子图层显示参数类。此类存储了各个子图层的名称、是否可见的状态、SQL 过滤条件等参数。 API详情&#xff1a;http://support.supermap.com.cn:8090/iserver/iClient/forJavaScript/docs/maplibregl/LayerStatus…

Kafka和RabbitMQ区别

RabbitMQ的消息延迟是微秒级&#xff0c;Kafka是毫秒级&#xff08;1毫秒1000微秒&#xff09; 延迟消息是指生产者发送消息发送消息后&#xff0c;不能立刻被消费者消费&#xff0c;需要等待指定的时间后才可以被消费。 Kafka的单机呑吐量是十万级&#xff0c;RabbitMQ是万级…

Webpack模式-Resolve-本地服务器

目录 ResolveMode配置搭本地服务器区分环境配置 Resolve 前面学习时使用了各种各样的模块依赖&#xff0c;这些模块可能来自于自己编写的代码&#xff0c;也可能来自第三方库&#xff0c;在 Webpack 中&#xff0c;resolve 是用于解析模块依赖的配置项&#xff0c;它决定了 We…

特权访问管理阻力最小的途径

特权访问管理 (PAM) 已经存在 20 多年&#xff0c;它将关键帐户放入保险库中&#xff0c;以确保只有特定人员才能安全地访问它们。 从那时起&#xff0c;PAM 不断发展&#xff0c;现在专注于控制访问本身&#xff0c;这意味着防止广泛访问特定数据&#xff0c;并提供有关谁有访…

JS基础练习|点击按钮更改背景色

效果图 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><link rel"…

《易泊车牌识别相机:精准识别的智能之选》

在如今的智能交通领域&#xff0c;车牌识别技术起着至关重要的作用。而易泊车牌识别相机以其卓越的性能脱颖而出。 易泊车牌识别相机的识别率极高。无论是在白天还是夜晚&#xff0c;无论是清晰的车牌还是稍有磨损的车牌&#xff0c;它都能迅速准确地识别出车牌号码。这得益于其…

多区域OSPF路由协议

前言 之前也有过关于OSPF路由协议的博客&#xff0c;但都不是很满意&#xff0c;不是很完整。现在也是听老师讲解完OSPF路由协议&#xff0c;感触良多&#xff0c;所以这里重新整理一遍。这次应该是会满意的 一些相关概念 链路状态 链路指路由器上的一个接口&#xff0c;链路状…

C++ | Leetcode C++题解之第452题用最少数量的箭引爆气球

题目&#xff1a; 题解&#xff1a; class Solution { public:int findMinArrowShots(vector<vector<int>>& points) {if (points.empty()) {return 0;}sort(points.begin(), points.end(), [](const vector<int>& u, const vector<int>&…

【重学 MySQL】五十一、更新和删除数据

【重学 MySQL】五十一、更新和删除数据 更新数据删除数据注意事项 在MySQL中&#xff0c;更新和删除数据是数据库管理的基本操作。 更新数据 为了更新&#xff08;修改&#xff09;表中的数据&#xff0c;可使用UPDATE语句。UPDATE语句的基本语法如下&#xff1a; UPDATE ta…

秒懂Linux之线程

目录 线程概念 线程理解 地址空间&#xff08;页表&#xff0c;内存&#xff0c;虚拟地址&#xff09; 线程的控制 铺垫 线程创建 ​编辑 线程等待 线程异常 线程终止 代码 线程优点 线程缺点 线程特点 线程概念 线程是进程内部的一个执行分支&#xff0c;线程是C…

【转载翻译】消息队列 - ActiveMQ、RabbitMQ、Kafka、ZeroMQ

转载自本人博客&#xff1a;【转载翻译】消息队列 - ActiveMQ、RabbitMQ、Kafka、ZeroMQ 转载自&#xff1a;The System Design Cheat Sheet: Message Queues - ActiveMQ, RabbitMQ, Kafka, ZeroMQ 本文由 Aleksandr Gavrilenko 发布于2023年12月21日 1. 前言 消息队列是异步服…

TypeScript 算法手册 【归并排序】

文章目录 1. 归并排序简介1.1 归并排序定义1.2 归并排序特点 2. 归并排序步骤过程拆解2.1 分割数组2.2 递归排序2.3 合并有序数组 3. 归并排序的优化3.1 原地归并排序3.2 混合插入排序案例代码和动态图 4. 归并排序的优点5. 归并排序的缺点总结 【 已更新完 TypeScript 设计模式…