【C++11】std::async函数介绍及问题梳理

目录

🌞1. std::async 简介

🌞2. 问题梳理

🌊2.1 std::async 到 future get 直接调用会如何抛异常

🌊2.2 std::async 如果通过劫持让 new 内存不够,有没有可能抛异常

🌊2.3 std::async 如果系统线程不够有没有可能异常


🌞1. std::async 简介

std::async 是 C++11 标准库中用于异步执行的函数,会返回一个 std::future 对象,以获取函数的执行结果。可用其在新线程中执行函数,也可以在当前线程中执行。std::async 的函数声明形式通常如下:

template <typename F, typename... Args>
std::future<typename std::result_of<F(Args...)>::type> std::async(std::launch policy, F&& f, Args&&... args);

说明:

  • template <typename F, typename... Args>:函数模板声明。F 是要执行的函数类型,Args... 是函数参数类型的模板参数包(variadic template parameter)。这意味着函数可以接受任意数量的参数。

  • std::future<typename std::result_of<F(Args...)>::type>:这是 std::async 函数的返回类型。它是一个 std::future 对象,包装了函数 F 的返回类型。std::future 允许在未来的某个时间点获取函数的执行结果。

  • std::async(std::launch policy, F&& f, Args&&... args):这是函数 std::async 的声明。它接受三个参数:

    • policy: std::launch 类型的参数,表示函数执行的策略。
      可以是 std::launch::async(在新线程中异步执行)或 std::launch::deferred(延迟执行,在调用 std::future::get()std::future::wait() 时执行)。
    • f:通用引用(universal reference),表示要执行的函数对象。通用引用允许 f 接受任意类型的参数。
    • args:这是函数 f 的参数列表。可以是零个或多个参数。
  • 这个函数的作用是根据给定的执行策略异步执行函数 f,并返回一个 std::future 对象,可用来等待函数的执行完成并获取函数的结果。

注意:

std::async 的行为受到执行策略参数 std::launch 类型的参数的影响,可能在调用时立即执行,也可能延迟到 std::future::get() std::future::wait() 被调用时才执行。


🌞2. 问题梳理

🌊2.1 std::async 到 future get 直接调用会如何抛异常

std::asyncstd::future::get 直接调用会抛出异常,主要有两种情况:

  1. 函数对象抛出异常。
  2. 如果使用 std::launch::async 策略,并在调用 std::future::get 之前函数执行抛出了异常,这种情况下会导致 std::future::get 抛出 std::future_error 异常。

【示例1】函数对象抛出异常

#include <iostream>
#include <future>// 抛出异常
void task1() {throw std::runtime_error("An error occurred in task1()");
}int main() {try {// 使用 std::async 启动一个异步任务auto future1 = std::async(std::launch::async, task1);// 等待异步任务的完成并获取结果future1.get(); // 这里会抛出异常} catch (const std::exception& e) {std::cerr << "Caught exception: " << e.what() << std::endl;}return 0;
}//输出内容:
Caught exception:An error occurred in task1()

该示例中,task1 函数会抛出异常。当调用 future1.get() 时,如果 task1 函数抛出异常,std::future::get 也会抛出异常。

【示例2】使用 std::launch::async 策略并在函数执行前抛出异常

#include <iostream>
#include <future>
#include <thread>
#include <chrono>// 在函数执行前抛出异常的函数
void task2() {// 人为延迟,增加在调用 std::future::get 前抛出异常的机会std::this_thread::sleep_for(std::chrono::milliseconds(100));throw std::runtime_error("An error occurred in task2()");
}int main() {try {// 使用 std::async 启动一个异步任务,使用 std::launch::async 策略auto future2 = std::async(std::launch::async, task2);// 在get函数执行前抛出异常throw std::runtime_error("An error occurred before calling future2.get()");// 等待异步任务的完成并获取结果future2.get(); // 这里会抛出 std::future_error 异常} catch (const std::exception& e) {std::cerr << "Caught exception: " << e.what() << std::endl;}return 0;
}//输出内容:
Caught exception: An error occurred before calling future2.get()

该示例中,task2 函数会在std::future::get 函数执行前抛出异常。

main 函数中,虽然调用 future2.get() 前手动抛出了异常,但是由于使用了 std::launch::async 策略,task2 函数会在新线程中执行std::future::get() 调用则在当前线程(即主线程)中执行】。因此,即使在主线程中抛出了异常,新线程中的任务函数也会继续执行:std::future::get 会等待 task2 函数执行完成【含加入的延时:100毫秒】,然后抛出 std::future_error 异常,说明在获取结果之前已经发生异常。


🌊2.2 std::async 如果通过劫持让 new 内存不够,有没有可能抛异常

std::async 不会直接抛出异常来处理内存不足的情况。在 C++ 中,当 new 操作符无法分配所需的内存时,会抛出 std::bad_alloc 异常。但std::async 不会直接抛出该异常。

std::async 中,任务可能在一个新线程中执行,也可能在当前线程中执行。如果任务在新线程中执行,并且在该新线程中发生了内存分配失败,那么系统会终止整个程序,而不是将异常传递回调用 std::async 的地方。【这是因为线程的异常不能跨线程传递】

这是因为C++的异常处理机制不能跨线程传播。当一个异常在一个线程中被抛出,而没有被捕获时,它会导致这个线程终止。如果异常发生在 std::async 创建的新线程中,并且在那里没有被捕获,那么整个线程会终止,但异常不会被传递回调用 std::async 的线程。

所以,虽然劫持 new 可以模拟内存不足的情况,但由于异常处理机制的限制,std::async 并不能捕获由于新线程中的内存分配失败而导致的异常。

所以,如果在 std::async内部发生了内存分配失败,程序通常会终止并可能会生成错误报告,而不是抛出异常到 std::async 的调用者。因此,对于内存不足的情况,最好在程序中进行适当的内存管理和异常处理,而不是依赖于 std::async 来处理此类问题。

【示例1】系统内存不足导致异常

#include <iostream>
#include <future>
#include <vector>
#include <cstdlib>void task() {// 尝试分配大量内存,可能导致内存不足std::vector<int> v(1000000000); // 尝试分配 4 GB 的内存
}int main() {try {// 尝试启动一个异步任务auto future = std::async(std::launch::async, task);// 等待异步任务的完成并获取结果future.get();} catch (const std::exception& e) {std::cerr << "Caught exception: " << e.what() << std::endl;}return 0;
}//输出内容:
Caught exception:bad allocation

该示例中,task 函数尝试分配大量内存。如果系统内存不足,std::vector 的构造函数将抛出 std::bad_alloc 异常。由于这个异常没有在 task 函数内部被捕获,因此异常会传播到 std::async 调用处,进而抛出 std::system_error 异常。

【示例2】劫持 new 让 new 内存不够抛异常

#include <iostream>
#include <future>void* operator new(size_t size) {std::cout << "Overloaded new called with size: " << size << std::endl;// 模拟内存不足的情况,分配失败throw std::bad_alloc();
}int main() {try {// 调用std::async,启动一个异步任务auto future = std::async(std::launch::async, [](){// 在这个异步任务中进行一些内存分配操作int* ptr = new int[100000000]; // 尝试分配非常大的内存delete[] ptr;});// 获取异步任务的结果future.get();} catch(const std::bad_alloc& e) {// 捕获异常并输出错误信息std::cerr << "Caught bad_alloc exception: " << e.what() << std::endl;}return 0;
}//输出内容:
Overloaded new called with size: 176
Caught bad_alloc exception:bad allocation

该示例中,重载 new 运算符,使其抛出 std::bad_alloc 异常,而不是实际分配内存。在 task 函数内部,尝试分配大量内存,并捕获了 std::bad_alloc 异常。由于 new 运算符的劫持,内存分配失败时会抛出异常。这个异常会在 std::async 调用处被捕获。


🌊2.3 std::async 如果系统线程不够有没有可能异常

这种情况下,std::async 可能会抛出 std::system_error 异常。

在使用 std::async 时,如果系统线程不够,可能会导致无法启动新线程而引发异常。这通常不是由于内存不足引起的,而是由于达到了系统对同时运行线程数量的限制。

【示例】系统线程不够抛异常

#include <iostream>
#include <future>
#include <vector>
#include <thread>
#include <chrono>void task() {// 模拟一个耗时的任务std::this_thread::sleep_for(std::chrono::seconds(2));std::cout << "Task executed in thread: " << std::this_thread::get_id() << std::endl;
}int main() {try {std::vector<std::future<void>> futures;// 启动多个异步任务for (int i = 0; i < 10000000000000; ++i) {futures.push_back(std::async(std::launch::async, task));}// 等待所有异步任务完成for (auto& future : futures) {future.get();}} catch(const std::system_error& e) {// 捕获系统错误异常并输出错误信息std::cerr << "Caught system_error exception: " << e.what() << std::endl;}return 0;
}

该示例启动了多个异步任务,每个任务执行一个模拟的耗时操作。如果系统没有足够的线程资源来启动这些线程,会抛出 std::system_error 异常。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电商平台接口自动化框架实践||电商API数据采集接口

电商数据采集接口 语言&#xff1a;python 接口自动化实现流程 红色为可实现/尚未完成 绿色为需要人工干预部分 自动生成测试用例模板&#xff08;俩种方式二选一&#xff09;&#xff1a; mimproxy&#xff0c;通过浏览器代理抓包方式&#xff0c;访问 H5 或者 web 页面&a…

万物生长大会 | 创邻科技再登杭州准独角兽榜单

近日&#xff0c;由民建中央、中国科协指导&#xff0c;民建浙江省委会、中国投资发展促进会联合办的第八届万物生长大会在杭州举办。 在这场创新创业领域一年一度的盛会上&#xff0c;杭州市创业投资协会联合微链共同发布《2024杭州独角兽&准独角兽企业榜单》。榜单显示&…

【强化学习-Mode-Free DRL】深度强化学习如何选择合适的算法?DQN、DDPG、A3C等经典算法Mode-Free DRL算法的四个核心改进方向

【强化学习-DRL】深度强化学习如何选择合适的算法&#xff1f; 引言&#xff1a;本文第一节先对DRL的脉络进行简要介绍&#xff0c;引出Mode-Free DRL。第二节对Mode-Free DRL的两种分类进行简要介绍&#xff0c;并对三种经典的DQL算法给出其交叉分类情况&#xff1b;第三节对…

Tomcat的实现

在一台电脑上启动tomcat&#xff0c;tomcat是server&#xff0c;即服务器。服务器只会被实例化一次&#xff0c;tomcat这只猫就是服务器。服务器下包含多个子节点服务&#xff0c;即service&#xff0c;顾名思义就是对外提供服务。服务器通常只有一个服务&#xff0c;默认是卡特…

申请免费的域名证书

免费域名证书主要是由一些证书颁发机构&#xff08;CA&#xff09;提供的&#xff0c;用于为网站启用HTTPS加密的数字证书&#xff0c;目的是保障网站数据传输的安全性。这些证书的特点和获取途径如下&#xff1a; 功能与目的&#xff1a;免费域名证书能够帮助网站实现基本的加…

Edge浏览器自动翻译功能按钮不见了

前言&#xff1a; 平时偶尔会用到Edge的页面翻译功能&#xff0c;使用挺方便。突然发现Edge浏览器的翻译功能不见 了。如下图所示&#xff1a; 解决思路&#xff1a; 1、从网上找各种解决方案也没有解决&#xff0c;其中有一个说到点右上角的三个点 2、点击设置…

有哪些值得买的开放式耳机推荐?2024年开放式运动耳机选购指南

开放式耳机因其独特设计&#xff0c;能在一定程度上保护听力。相较于传统封闭式耳机&#xff0c;开放式设计允许周围环境声音自然流入耳内&#xff0c;降低了耳内共振和声压&#xff0c;减少了耳道的不适感&#xff0c;从而减轻了对听力的潜在损害。对于追求音质与听力保护并重…

均线金叉死叉及应用案例

5日均线和10日均线交叉 5日均线和10日均线交叉指的是5日和10日均线的相互交汇,根据5日均线运行方向的不同可以分为两种交叉。一是5日均线向上运行并交叉10日均线,二是5日均线向下运行并交叉10日均线,前面的交叉被称为金叉,后面的交叉被称为死叉。 技能解析: 5日均线和10日均…

N5183B是德科技n5183b信号源

181/2461/8938产品概述&#xff1a; 简  述&#xff1a; N5183B 频率范围&#xff1a;9 kHz 至 20 GHz&#xff0c;具有 AM、FM、相位调制功能。N5183B MXG X 系列微波模拟信号发生器拥有 9 kHz 至 40 GHz 的频率覆盖范围&#xff0c;以及接近 PSG 级别的相位噪声性能&…

Qt---绘图和绘图设备

一、QPainter绘图 绘图事件 void paintEvent() 声明一个画家对象&#xff0c;OPainter painter(this) this指定绘图设备 画线、画圆、画矩形、画文字 设置画笔QPen 设置画笔宽度、风格 设置画刷QBrush 设置画刷风格 代码示例&#xff1a; #includ…

以大开放促进大开发 | 陕西粮农集团携手开源网安引领新时代西部大开发

​5月13日&#xff0c;开源网安与陕西粮农集团成功签署战略合作协议。双方将在网络安全保障体系建设及人才培养领域展开深度合作&#xff0c;共同筑牢陕西省数字经济建设安全屏障。陕西省粮农信息技术有限公司总经理解玮峰、陕西省粮农信息技术有限公司安全事业部负责人马德君、…

软件测试之 自动化测试 基于Python语言使用Selenium、ddt、unitTest 实现自动化测试

你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github gitee 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我会尽力带来有趣的内容 本文档是一年前学后记得笔记 现在居然还记得很清楚 基于…

渗透神器:burpsuit教程

前言&#xff1a;释疑解惑 《BP使用教程一》发布后&#xff0c;后台收到了许多小伙伴的私信问BP是怎么汉化的&#xff0c;在这里统一为大家解答一下。 BP的汉化依赖于汉化jar包&#xff0c;在启动时引入汉化包即可&#xff0c;废话不多说&#xff0c;直接上命令&#xff1a; …

【强训笔记】day18

NO.1 思路&#xff1a;双指针模拟。to_string将数字转化为字符。 代码实现&#xff1a; class Solution { public:string compressString(string param) {int left0,right0,nparam.size();string ret;while(right<n){while(right1<n&&param[right]param[right…

VBA在Excel中登录页面的应用—动态密码设置

https://mp.weixin.qq.com/s?__biz=MzkwMzY1OTIzOA==&mid=2247484420&idx=1&sn=5f98ef156cd6a784f0b1e64eed11ee42&chksm=c093af8df7e4269bdda3ed4adc37ce5f30707760ad42a2e0c6c3278ff0a0c5fcaf890016f9b5&token=1012529499&lang=zh_CN#rd 文章目录 …

libcity 笔记:libcity/executor/traj_loc_pred_executor.py

1 构造函数 2 _build_optimizer 根据配置中指定的优化器类型创建并返回一个适合用于模型训练的优化器对象 3 _build_scheduler 构建一个学习率调度器&#xff08;scheduler&#xff09; 4 train 5 run 6 _valid_epoch 7 load_model & save_model 保存/加载模型的状态字…

文档加密软件大比拼:哪款更适合你

在数字时代的浪潮中&#xff0c;信息安全成为了每个人和企业都无法忽视的重要议题。文档加密软件作为保护数据安全的重要手段&#xff0c;其种类繁多&#xff0c;功能各异。本文将带您走进文档加密软件的世界&#xff0c;对比多款热门产品&#xff0c;助您找到最适合自己的加密…

程序员之路:裁员与内卷下的生存之道

作为一名普通的程序员&#xff0c;身处这个瞬息万变的IT行业&#xff0c;面对着今年不断加剧的裁员浪潮和日益激烈的内卷竞争&#xff0c;我时常感到焦虑和不安。然而&#xff0c;正是这些挑战&#xff0c;让我们更加深入地思考了在这个行业中&#xff0c;我们该如何找到自己的…

【MySQL数据库】初步认识数据库,实现基本操作

在信息爆炸的今天&#xff0c;数据无处不在&#xff0c;它们构成了互联网世界的基石。但数据本身若未经有效组织和管理&#xff0c;就如同散落在沙滩上的珍珠&#xff0c;难以发挥其真正的价值。这时&#xff0c;“数据库”这一概念便如同一根线&#xff0c;将这些珍珠串联起来…

【iOS】frame与bounds区别

文章目录 前言framebounds两者区别size的区别总结 前言 在学习响应者链的过程中用到了frame与bounds的混用&#xff0c;这两个属性经常出现在我们的开发中&#xff0c;特别撰写一篇博客分析区别 首先&#xff0c;我们来看一下iOS特有的坐标系&#xff0c;在iOS坐标系中以左上…