C++ 异步编程的利器std::future和std::promise
1、背景
在现代计算机系统中,许多任务可能需要花费较长时间才能完成,例如网络请求、文件读取、大规模数据计算等。如果在程序中同步地执行这些任务,会导致主线程被阻塞,整个程序在任务执行期间无法响应其他操作,用户体验极差。异步编程应运而生,它允许程序在执行耗时任务的同时,不影响其他部分的正常运行,提高了程序的效率和灵活性。这里的其他部分通常运行在不同的cpu核上,当在其它核上的任务执行完毕,通知当前线程即可,当前线程可以在这段时间片执行其它的任务。
2、std::future的基本概念与用法
std::future 可以看作是一个 “占位符” 或者 “结果容器”,用于获取异步操作的结果。它提供了一种机制,使得当前线程能够在未来的某个时刻获取另一个线程中执行的异步任务的返回值。
2.1、获取异步任务结果
我们可以通过 std::async 函数来启动一个异步任务,并返回一个 std::future 对象。
#include <iostream>
#include <future>
#include <thread>int add(int a, int b) {std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作return a + b;
}int main() {std::future<int> result = std::async(add, 3, 5);// 在此处可以执行其他操作,不会被 add 函数的执行阻塞int sum = result.get(); // 获取异步任务的结果,此时如果任务尚未完成,会阻塞当前线程直到任务完成std::cout << "The sum is: " << sum << std::endl;return 0;
}
在上述代码中,std::async 启动了 add 函数的异步执行,并返回一个 std::future 对象 result。在 result.get() 被调用时,如果 add 函数尚未完成,主线程会被阻塞等待结果。一旦 add 函数执行完毕,result.get() 就会获取到返回值并赋值给 sum。
2.2、等待异步任务完成
除了使用 get 方法获取结果时阻塞等待,std::future 还提供了 wait 方法来单纯地等待异步任务完成,而不获取结果。
#include <iostream>
#include <future>
#include <thread>void print_hello() {std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "Hello from the async task!" << std::endl;
}int main() {std::future<void> result = std::async(print_hello);std::cout << "Before waiting..." << std::endl;result.wait(); // 等待异步任务完成std::cout << "After waiting..." << std::endl;return 0;
}
在这个例子中,print_hello 函数在异步线程中执行,主线程通过 result.wait() 等待其完成。在等待期间,主线程可以执行其他非依赖于 print_hello 结果的操作,但在 wait 调用处会暂停,直到异步任务结束。
2.3、查询异步任务状态
std::future 还提供了 wait_for 方法来设置一个等待时间,用于查询异步任务是否在指定时间内完成。它返回一个 std::future_status 类型的枚举值,有以下几种情况:
- std::future_status::ready:表示异步任务已经完成,可以获取结果。
- std::future_status::timeout:表示在等待的时间内任务未完成。
- std::future_status::deferred:表示异步任务被延迟执行,只有在调用 get 时才会真正开始执行。
#include <iostream>
#include <future>
#include <thread>
#include <chrono>int multiply(int a, int b) {std::this_thread::sleep_for(std::chrono::seconds(3));return a * b;
}int main() {std::future<int> result = std::async(multiply, 4, 6);std::cout << "Checking task status..." << std::endl;std::future_status status = result.wait_for(std::chrono::seconds(2));if (status == std::future_status::ready) {std::cout << "Task completed. Result: " << result.get() << std::endl;} else if (status == std::future_status::timeout) {std::cout << "Task still in progress. Waiting more..." << std::endl;int product = result.get(); // 再次等待并获取结果std::cout << "Task completed. Result: " << product << std::endl;} else if (status == std::future_status::deferred) {std::cout << "Task is deferred." << std::endl;}return 0;
}
这个例子中,设置了 2 秒的等待时间。由于 multiply 函数实际需要 3 秒才能完成,所以在 2 秒时 wait_for 返回 std::future_status::timeout,然后我们再次调用 result.get() 来获取最终结果并等待任务完成。
3、std::promise 的基本概念与用法
std::promise 则用于设置异步操作的结果或异常,它与 std::future 紧密配合,实现了异步任务结果的传递。
3.1、设置异步任务结果
我们可以在一个线程中通过 std::promise 设置结果,而在另一个线程中通过对应的 std::future 获取该结果。
#include <iostream>
#include <future>
#include <thread>void set_value_in_thread(std::promise<int>& prom) {std::this_thread::sleep_for(std::chrono::seconds(1));prom.set_value(42); // 设置异步任务的结果值为 42
}int main() {std::promise<int> prom;// 建立关联std::future<int> fut = prom.get_future();std::thread t(set_value_in_thread, std::ref(prom));int value = fut.get(); // 获取设置的结果值std::cout << "The value set in the other thread is: " << value << std::endl;t.join();return 0;
}
在上述代码中,set_value_in_thread 函数在一个新线程中运行,通过传入的 std::promise 对象 prom 设置了结果值为 42。在主线程中,通过 fut.get() 获取到了这个结果值。
3.2、设置异步任务异常
std::promise 不仅可以设置结果值,还可以设置异常。
#include <iostream>
#include <future>
#include <thread>
#include <exception>void set_exception_in_thread(std::promise<int>& prom) {try {throw std::runtime_error("An error occurred in the async thread.");} catch (...) {prom.set_exception(std::current_exception()); // 设置异步任务的异常}
}int main() {std::promise<int> prom;std::future<int> fut = prom.get_future();std::thread t(set_exception_in_thread, std::ref(prom));try {fut.get(); // 尝试获取结果,会抛出之前设置的异常} catch (const std::runtime_error& e) {std::cout << "Caught exception: " << e.what() << std::endl;}t.join();return 0;
}
在这个例子中,set_exception_in_thread 函数在异步线程中抛出了一个异常,并通过 prom.set_exception(std::current_exception()) 将异常设置到 std::promise 对象中。在主线程中,当调用 fut.get() 时,就会捕获到这个异常并进行处理。
4、std::future 和 std::promise 的关联
std::future 和 std::promise 的关联是通过 std::promise 的 get_future 方法建立的。一旦建立关联,一个线程可以通过 std::promise 设置结果或异常,而另一个线程可以通过对应的 std::future 获取结果或处理异常。这种协同工作机制使得我们可以在不同的线程之间进行高效的结果传递和错误处理,非常适合构建复杂的异步任务处理流程。例如,在一个多线程的网络服务器中,一个线程负责接收网络请求并将处理任务分配给工作线程,工作线程完成任务后可以通过 std::promise 将结果返回给主线程,主线程再通过 std::future 获取结果并响应给客户端。
5、注意事项
- 一个 std::promise 对象只能设置一次结果或异常。一旦设置完成,再次调用 set_value 或 set_exception 将会抛出 std::future_error 异常。因此,在使用时要确保正确地设置结果或异常,避免重复设置。
- 在使用 std::future 和 std::promise 时,要注意线程之间的同步和资源管理。例如,在获取 std::future 的结果时,如果异步任务尚未完成,当前线程会被阻塞,这可能会影响程序的性能和响应性。因此,可以合理地结合 wait_for 等方法来进行超时处理或轮询检查任务状态,避免长时间的阻塞。同时,在创建线程和使用 std::promise 等对象时,要确保正确地管理线程资源,避免出现资源泄漏等问题。例如,在使用 std::thread 时,要记得在合适的时机调用 join 或 detach 方法。
- 在异步任务中抛出的异常,如果没有通过 std::promise 正确地设置到对应的 std::future 中,可能会导致异常被丢失或程序出现未定义行为。因此,要确保在异步任务中捕获所有可能的异常,并通过 set_exception 方法将异常传递给 std::future,以便在获取结果时能够正确地处理异常。
相关文章:
C++ 异步编程的利器std::future和std::promise
1、背景 在现代计算机系统中,许多任务可能需要花费较长时间才能完成,例如网络请求、文件读取、大规模数据计算等。如果在程序中同步地执行这些任务,会导致主线程被阻塞,整个程序在任务执行期间无法响应其他操作,用户体…...
CRM 系统中的 **知识库功能** 的设计与实现
CRM 系统中的 **知识库功能** 旨在为用户提供一个集中的平台,用于存储、组织和管理有关系统功能、常见问题、使用技巧、操作文档等信息。它能够帮助用户高效解决问题、快速获取所需信息,从而提升使用体验并减少客户支持负担。 ### 一、知识库功能的设计…...
重学设计模式-工厂模式(简单工厂模式,工厂方法模式,抽象工厂模式)
在平常的学习和工作中,我们创建对象一般会直接用new,但是很多时候直接new会存在一些问题,而且直接new会让我们的代码变得非常繁杂,这时候就会巧妙的用到设计模式,平常我们通过力扣学习的算法可能并不会在我们工作中用到…...
【C语言】结构体(四)
本篇重点是typedef关键字 一,是什么? typedef用来定义新的数据类型,通常typedef与结构体的定义配合使用。 简单来说就是取别名 ▶ struct 是用来定义新的数据类型——结构体 ▶ typedef是给数据类型取别名。 二,为什么…...
swift类方法为什么使用表派发?
直接上答案:因为表派发允许子类重写父类的方法,并在运行时根据对象的实际类型调用正确的方法实现。 什么是表派发? 首先我们先知道的是,swift当中函数的派发机制主要分为静态派发和动态派发。动态派发又分为表派发和消息派发。 …...
php实现AES/CBC/PKCS5Padding加密
接口文档 文档给过来的案例是java程序的,参照其思路,造一个php版本 构造aes对称加密 public static function encry($data){$data "要加密的数据";$key 你的256位密钥; // 密钥应该是16字节(128位),24字节…...
Anaconda3安装及使用
Anaconda3安装及使用 Linux中安装Anaconda31.安装 Anaconda32.配置环境变量3.验证是否成功 Conda环境和包管理1.Conda 环境初始化2.Conda Env 管理3.Conda 软件包管理 Linux中安装Anaconda3 下面是在Linux中安装Anaconda3-2021.05的教程,其他版本Anaconda更换名字即…...
Argon2-cffi与argon2-cffi-bindings:深入理解及其应用
Argon2-cffi与argon2-cffi-bindings的关系 在Python密码学领域,argon2-cffi和argon2-cffi-bindings是两个经常被提及的库。尽管它们的名字相似,但它们在实现和用途上有所不同。argon2-cffi是一个提供Argon2哈希算法的Python库,而argon2-cffi-…...
spring boot+jpa接入达梦数据库
文章目录 前言依赖配置对应的domain类和repository 前言 最近有一个新项目,由于信息安全等要求只能使用达梦数据库(dm8),之前从来没用过,特此开一个笔记记录一下spring bootjpa如何使用达梦数据库完成开发。 依赖 p…...
Vite构建,用NodeJS搭建一个简单的Vite服务
Vite 是一个现代的前端构建工具,由 Vue.js 作者尤雨溪创建。它主要用于开发和构建现代 JavaScript 应用,尤其是单页应用(SPA)。Vite 相比于传统的构建工具(如 Webpack)有几个显著的优势: 即时开…...
R语言机器学习论文(六):总结
文章目录 介绍参考文献介绍 本文采用R语言对来自进行数据描述、数据预处理、特征筛选和模型构建。 最后我们获得了一个能有效区分乳腺组织的随机森林预测模型,它的性能非常好,这意味着它可能拥有非常好的临床价值。 在本文中,我们利用R语言对来自美国加州大学欧文分校的B…...
python---面向对象---综合案例(4)
案例描述 实现加减乘法运算 # _*_ encoding:utf-8 _*_# 计算器, 实现一些基本的操作, 加减乘除运算, 以及打印结果操作# ------------------------------------代码1-------------------------------------- def jia(n1, n2):return n1 n2def jian(n1, n2):return n1 - n2de…...
如何参加华为欧拉考试?
华为欧拉考试主要针对的是华为欧拉(EulerOS/openEuler)操作系统的认证考试,这一认证体系旨在培养和认证具备基于欧拉操作系统进行企业级应用运行基础环境搭建、管理和调测能力的工程师以及云计算架构师。以下是对华为欧拉考试的详细介绍&…...
算法预刷题Day9:BM28 二叉树的最大深度
描述: 描述 求给定二叉树的最大深度, 深度是指树的根节点到任一叶子节点路径上节点的数量。 最大深度是所有叶子节点的深度的最大值。 (注:叶子节点是指没有子节点的节点。) 思路: 当前节点的最大高度 ma…...
exp_lr_scheduler理解
1. exp_lr_scheduler理解 这行代码定义了一个学习率调度器,用于动态调整训练过程中优化器的学习率。让我们分解并解释其含义: 1. exp_lr_scheduler 是什么? exp_lr_scheduler 是一个 学习率调度器(LR Scheduler),由 torch.optim.lr_scheduler.StepLR 创建,旨在按照预…...
Algorithm:河内之塔
1. 说明 河内之塔(Towers of Hanoi)是法国人 M.Claus(Lucas)于1883年从泰国带至法国的,河内为越战时北越的首都,即现在的胡志明市;1883年法国数学家 Edouard Lucas 曾提及这个故事,据…...
集中管理与实时审计:构建Linux集群(1300台服务器)日志平台的最佳实践
简介 随着企业IT基础设施的不断扩大,Linux服务器的数量也日益增多,传统的单机日志管理方式已无法满足对日志数据集中管理、审计和分析的需求。尤其是在大型集群环境中,如何高效地收集、存储和分析日志成为了一项重要的技术挑战。 背景 在实…...
在Scala中Array不可变的学习
package gjhs114import scala.collection.mutable.ArrayBuffer object Arrray114 {// 不可变数组:Array// def main(args: Array[String]): Unit {1 创建不可变数组// val arr1 Array(1,2,3)//2 访问.数组名(下标)。下标是从0开始到…...
vue3+vite 批量引入组件动态使用
import { ref, reactive, toRaw, markRaw, defineAsyncComponent, onMounted } from vue import type { Component } from vue// vue3vite 批量引入组件动态使用 const modules import.meta.glob<Component>(./details/*.vue) // 明确指定导入的模块类型为Component con…...
设计模式——方法链or流式接口
方法链或流式接口是一种编程模式或设计模式。核心思想是通过返回对象自身的应用,使得可以在一个表达式中连续调用多个方法。 c中实现这种模式 1.基本语法规则 (1)每个可链接的方法都返回对象自身的引用(通常是*this)…...
效率直接起飞!2026年最值得信赖的专业AI论文软件
2026年AI论文写作工具已从“内容生成”升级为智能学术辅助系统,核心评价维度包括文献真实性、格式合规性、长文本逻辑、查重降重、AIGC合规与多语言支持。本次测评覆盖6款主流工具,测试场景涵盖中英文论文、全流程与专项功能、免费与付费版本,…...
Lindy企业流程自动化实施全周期拆解:从0到1上线仅需14天的关键5步法
更多请点击: https://intelliparadigm.com 第一章:Lindy企业流程自动化实施全周期拆解:从0到1上线仅需14天的关键5步法 Lindy 作为轻量级、高可扩展的流程自动化平台,其核心优势在于将复杂的企业级RPA与低代码逻辑深度融合&#…...
非结构化数据处理有没有更高效的办法?2026智能体端到端方案彻底终结数据孤岛
在2026年的数字化深水区,企业面对的不再是单纯的数据库增删改查,而是由海量PDF合同、非标图片、多模态音视频、复杂的系统日志以及社交媒体碎片信息构成的“非结构化数据冰山”。 据行业数据显示,企业内部超过80%的数据以非结构化形式存在。过…...
30个专业模板轻松美化Power BI报表:零基础也能打造惊艳数据可视化
30个专业模板轻松美化Power BI报表:零基础也能打造惊艳数据可视化 【免费下载链接】PowerBI-ThemeTemplates Snippets for assembling Power BI Themes 项目地址: https://gitcode.com/gh_mirrors/po/PowerBI-ThemeTemplates 还在为Power BI报表设计发愁吗&a…...
深度评测2026年TOP10降AI率平台:找到导师推荐的“无痕降AIGC”终极方案
AI写作工具的兴起让论文写作和内容创作变得前所未有的高效,许多学生和职场人都开始依赖这类工具来提升效率、节省时间。然而,随着技术发展,高校、平台和期刊对AI生成内容的检测标准也在不断提高,越来越多的用户发现,自…...
终极游戏库管理器Playnite:一站式管理20+平台游戏的最佳解决方案
终极游戏库管理器Playnite:一站式管理20平台游戏的最佳解决方案 【免费下载链接】Playnite Video game library manager with support for wide range of 3rd party libraries and game emulation support, providing one unified interface for your games. 项目…...
不会 CSS 也能做出惊艳 PPT!Frontend Slides这个开源 Claude Code 技能让 AI 帮你生成 12 种风格演示文稿,告别千篇一律的紫渐变
不会 CSS 也能做出惊艳 PPT!Frontend Slides这个开源 Claude Code 技能让 AI 帮你生成 12 种风格演示文稿,告别千篇一律的紫渐变 💡 每次做 PPT 都在 Powerpoint 里拖来拖去,最后做出来还是那个味儿?Frontend Slides 让…...
3DS原生GBA硬件实战指南:open_agb_firm深度解析与高效方案
3DS原生GBA硬件实战指南:open_agb_firm深度解析与高效方案 【免费下载链接】open_agb_firm open_agb_firm is a bare metal app for running GBA homebrew/games using the 3DS builtin GBA hardware. 项目地址: https://gitcode.com/gh_mirrors/op/open_agb_firm…...
终极指南:如何用WeChatExporter永久备份微信聊天记录,打造你的数字记忆宝库
终极指南:如何用WeChatExporter永久备份微信聊天记录,打造你的数字记忆宝库 【免费下载链接】WeChatExporter 一个可以快速导出、查看你的微信聊天记录的工具 项目地址: https://gitcode.com/gh_mirrors/wec/WeChatExporter 你是否曾有过这样的经…...
如何在Windows上让DualShock 3控制器重获新生?DsHidMini虚拟HID驱动技术解析
如何在Windows上让DualShock 3控制器重获新生?DsHidMini虚拟HID驱动技术解析 【免费下载链接】DsHidMini Virtual HID Mini-user-mode-driver for Sony DualShock 3 Controllers 项目地址: https://gitcode.com/gh_mirrors/ds/DsHidMini 在Windows平台使用索…...
