「C++」掌握C++异步编程

news2024/11/24 1:59:47
在这里插入图片描述

💻文章目录

  • 📄前言
  • 异步任务
    • 概念
    • 期待与承诺
      • future
      • promise
      • 异常处理
    • 执行异步任务
      • async
      • packaged_task
  • 📓总结


📄前言

异步任务是多线程编程的核心,若想学习多线程设计,深入了解这些基本概念是必不可少的。如果你从未了解过这些概念,亦或者对c++异步任务的库函数有所遗忘了,不妨点进本文来学习一下。

异步任务

概念

异步任务就是在程序后台执行的任务,而异步指的是与主线程所不同步奏。用生活的例子来讲就是,我在一边看视频,一边吃饭。创建一个线程,让它执行一个函数,这就是一个简单的异步任务。你可能会觉得这也太简单了,但先不要走,其实我们要讨论的还不止如此。

C++为异步任务提供的可不止thread,还有为了异步任务返回值而诞生的 期待(future) 承诺(promise),以及打包任务所需的 packaged_task 和 用更高级的异步任务创建工具—async

惯例地讨论下优缺点:

  • 异步任务的优点:
    • 提高程序的性能:多个线程并发运行能够显著提高程序的性能(前提是有合理的设计)。
    • 提高程序响应性:一个程序可能需要等待I/O输入的同时,能够同时处理后台的各种任务(如网络数据传输)。
  • 异步任务的缺点
    • 让程序调试难度提高:多线程的 bug 可能难以重现、甚至只会在特定的机器出现问题。
    • 可能会导致死锁、竞态条件等问题:不合理的设计可能会导致程序的性能提高不显著,甚至导致程序无响应。

期待与承诺

如果你有使用过 thread 函数,那么你肯定会发现它是无法通过函数返回值来查看运算结果,虽然可以通过引用传参来获取返回值,但这样获取的数据在多线程情况下还得自己解决数据二义性问题,而 future 和 promise 提供了一种线程安全且方便的返回方式

future

future 如同其名—期待,期待一个任务结果的获取,我们不需要立刻知道结果是否就绪,只需要在我们需要用到结果时才去访问。如果此时结果还未就绪,线程就会阻塞等待这个结果的获取。

用一个生活的小例子来比喻就是:我和朋友约定去公园一起玩,我在去公园的路上不知道朋友是否已经到达,只有我到了公园才知道,我当然会期待到公园的时候他就已经到达,但如果他还没到公园,我就在此地等待。相信这个例子已经足够说明期待的本质了,在C++中 future 一般与 async 、promise、package_task 等工具一起使用

简单的函数使用演示:

#include <future>
#include <iostream>
#include <thread>
int sum(int x, int y) {
    std::cout << "线程运行ing" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(3));	// 模拟运行任务中。
    std::cout << "线程结束" << std::endl;
    return x + y;
}

int main() {
    std::future<int> res = std::async(sum, 1, 9);	// async的返回值是一个future。详细将在后文介绍。
    std::cout << res.get() << std::endl;	// res还未待续,线程阻塞等待res。
    
    return 0;
}

promise

promise 与 future 也是一样的“人与其名”,承诺会给 future 一个结果,而这个结果可能会及时得到,也可能会非常晚才得到。promise 需要与 future 一同使用,一般通过传参使用。

函数使用:

void func(std::promise<std::string>& ip, std::promise<std::string>& msg) {
    // 假设进行网络连接
    std::this_thread::sleep_for(std::chrono::seconds(1));
    ip.set_value("8.8.8.8");    //获取客户端ip
    msg.set_value("Hello!");         // 获取客户端的信息.
}

int main() {
    std::promise<std::string> get_ip, get_msg;
    std::future<std::string> ip = get_ip.get_future();	// 绑定期待
    std::future<std::string> msg = get_msg.get_future();
    // 引用参数需要使用ref来保证其引用性质。
    std::thread t1(func, std::ref(get_ip), std::ref(get_msg));
    
    t1.detach();	//分离线程
    printf("[%s] %s", ip.get().c_str(), msg.get().c_str());

    return 0;
}

异常处理

future 和 promise可以用于接受异常,这也是它们的一大特点之一,如果使用 async 中发生异常,则异常会存储到它的返回值中,当调用 get() 时再次被抛出。当然使用promise也能够设置异常,然后让future 接收。

int func(int x, int y)
{
    if(y == 0)
        throw std::runtime_error("x / 0");
    else
        return x / y;
}

void errorfunc(std::promise<int>& ret)
{
    try
    {
        int res = func(29, 0);
        ret.set_value(res);
    }catch(const std::runtime_error& e)
    {   // promise 也能够储存异常
        ret.set_exception(std::current_exception() );
    }
}


int main() {
    try {
        std::promise<int> ret;
        std::future<int> f = ret.get_future();  //绑定promise
        std::thread(errorfunc, std::ref(ret)).detach();
        int result = f.get();   //异常在get()函数被抛出
        std::cout << result << std::endl;
    }catch(const std::runtime_error& e)
    {
        std::cerr << "error: " << e.what() << std::endl;
    }

    return 0;
}

执行异步任务

async

async 是 C++ 中更智能的一种创建线程的方式,它能够自动管理线程的生命周期,并且自动控制线程的 数量(程序线程过多将不会创建),它的返回值是一个带函数返回值的 future ,可以用它来得知函数的运行结果 或 函数发生的异常。

  • async 的优点:
    • 自动管理线程生命周期:线程不需要自己来管理,函数将自己管理。
    • 异常安全:future能接受函数中产生的异常

函数使用:

template <typename Iterator, typename T>
T parallel_accumulate(Iterator first, Iterator last, T init)
{
    //获取数组的长度
    const unsigned long length = std::distance(first, last);

    const unsigned long min_per_thread = 25; 
    // 分块计算数据
    if(length <= 25)    //递归终点 
        return std::accumulate(first, last, init); 
    else
    {
        Iterator mid_point = first + length / 2; 
        // async会自动处理线程的数量,不用担心线程无限开销。 
        std::future<T> first_half_result = 
                std::async(parallel_accumulate<Iterator, T>, mid_point, last, init);   //处理后半部分


        // 处理前半数据	(注意这里的init要换成T())
        return first_half_result.get() + std::accumulate(first, mid_point, T());

    }
}

int main() {
    std::vector<int> nums(100);
    // 从1开始为数组填充数据1~100
    std::iota(nums.begin(), nums.end(), 1);
	
    auto sum = parallel_accumulate(nums.begin(), nums.end(), 123);
//    auto sum = std::accumulate(nums.begin(), nums.end(), 123);

    return 0;
}

// 函数递归展开图

                                          [0-100)
                                             |
                      +-----------------------------------------+
                      |                                         |
                  [0-50)                                 [50-100)
                      |                                         |
          +-------------------+                      +-------------------+
          |                   |                      |                   |
      [0-25)             [25-50)                [50-75)             [75-100)
        |                   |                      |                   |
   std::accumulate   std::accumulate        std::accumulate     std::accumulate

packaged_task

packaged_task 是用于打包异步任务的工具,它可以对普通函数、类内函数、lambda函数进行打包,然后在另一个线程中进行运行,经常用于像线程池等需要打包任务的场景。

简单的函数使用:

// 函数使用
int func(int x, int y)
{
    return x + y;
}

int main() {
    std::packaged_task<int(int, int)> task1(func);	//包装普通函数
    std::packaged_task<int()> task2([&] { return func(2, 3); });	// 包装lambda表达式

    std::future<int> res1 = task1.get_future();	// packaged_task 返回值是future
    std::future<int> res2 = task2.get_future();

    std::thread t1(std::move(task1), 9, 9).detach;	
    std::thread t2(std::move(task2) ).detach;

    std::cout << res1.get() << ":" << res2.get() << std::endl;

    return 0;
}

📓总结

在C++中异步任务常用的工具有future、promise、async、packaged_task等,掌握它们对编写一个高效的多线程至关总要,希望本文能够对你有所帮助。

工具用途
std::async用于以简化的方式启动异步任务,自动管理线程生命周期,并自动控制线程数量,返回std::future对象以获取任务的执行结果或异常。
std::future提供一种机制来访问异步操作的结果。当异步操作完成时,可以通过std::future对象获取结果或捕获在异步操作中抛出的异常。
std::promise允许在某个线程中设置值或异常,这些值或异常将在未来某个时刻通过与之关联的std::future对象被其他线程访问。
std::packaged_task封装一个可调用对象,并允许其异步执行,同时提供一个std::future对象,以便获取该可调用对象的返回值或在执行过程中捕获的异常。

📜博客主页:主页
📫我的专栏:C++
📱我的github:github

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

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

相关文章

GPT-3.5和GPT-Plus的区别

GPT-3.5和GPT-Plus都是OpenAI开发的大型语言模型,但它们之间有一些区别: GPT-3.5就是大家熟知的ChatGPT GPT-Plus 是Open AI 的更强的AI模型GPT-4版本。两者区别是&#xff1a; 模型规模:GPT-Plus是GPT-3的一个更大版本,参数量更多。而GPT-3.5是GPT-3的一个优化版本,在参数量…

欧科云链:香港虚拟资产OTC合规在即,技术监管成市场规范关键

4月12日香港OTC发牌制度公众咨询结束后&#xff0c;欧科云链研究院在星岛日报发表专栏文章&#xff0c;分享对香港OTC市场的调研情况&#xff0c;并提出“技术监管是香港OTC及Web3生态走向规范的关键”。欧科云链研究院认为&#xff0c;随着OTC监管及虚拟资产现货ETF等事件向前…

ruoyi 代码生成

子模块 ruoyi-generator 导入表信息 导入表信息时系统会默认读取ruoyi-generator/src/main/resources/generator.yml路径下的配置,配置如下 # 代码生成 gen:# 作者author: ruoyi# 默认生成包路径 system 需改成自己的模块名称 如 system monitor toolpackageName: com.ruoy…

Proteus 8.17 2024年最新版 安装和汉化教程

Proteus是一款专业的电路设计和仿真软件&#xff0c;被广泛应用于电子设计、嵌入式系统开发等领域&#xff0c;是世界上唯一将电路仿真软件、PCB设计软件和虚拟模型仿真软件三合一的设计平台。 软件下载&#xff1a;长按识别关注“我爱单片机”公众号&#xff0c;回复“protues…

C语言进阶课程学习记录-第38课 - 动态内存分配

C语言进阶课程学习记录-第38课 - 动态内存分配 内存动态分配实验-malloc(0)实验实验-realloc和calloc小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 内存动态分配 实验-malloc(0) #include <st…

PMP报考别跟风!搞懂这些问题不踩坑!

1.PMP是什么&#xff1f; 1.PMP(Project ManagementProfessional)的中文全称是项目管理专业人士资格认证。该认证是由美国项目管理协会PMI在全球206个国家发起的针对项目经理的资格认证。 2.PMP认证是目前国际上项目管理领域认可度和含金量最高的证书。通过PMP就证明你的项目…

3.1 海思SS928开发 - 烧写工具 - ToolPlatform 安装及配置

3.1 烧写工具 - ToolPlatform 安装及配置 ToolPlatform 安装 进入到开发虚拟机&#xff0c;将文件 ~/hiss928/sdk/ema_2.0.2.2/pc/ToolPlatform/ToolPlatform-1.0.11-win32-x86_64.zip 拷贝至 PC 上。PC 要求安装了 win7 及以上的操作系统。解压压缩包 ToolPlatform-1.0.11-w…

ffmpeg入门

ffmpeg入——安装 Fmpeg地址 FFmpeg源码地址&#xff1a;https://github.com/FFmpeg/FFmpeg FFmpeg可执行文件地址&#xff1a;https://ffmpeg.org/download.html Windows平台 Windows平台下载解压后如图所示&#xff08;文件名称以-share结尾的是开发库&#xff09; FFmpeg…

当面试官的一些想法

前天被同事拉去面试了&#xff0c;一天面试很多人&#xff0c;很是疲惫。之前在小米的5年&#xff0c;也总是被组长拉去当面试官。虽然我个人已经对这种事很厌倦了&#xff0c;但在面试过程中获得的一些收获还是不错的。今天也就从面试官的角度来给一些小建议。 事先声明哈&…

【数学建模】机器人避障问题

已知&#xff1a; 正方形5的左下顶点坐标 ( 80 , 60 ) (80,60) (80,60)&#xff0c;边长 150 150 150机器人与障碍物的距离至少超过 10 10 10个单位规定机器人的行走路径由直线段和圆弧组成&#xff0c;其中圆弧是机器人转弯路径。机器人不能折线转弯&#xff0c;转弯路径由与…

单输入多输出(SIMO)和多输入多输出(MIMO)模型

我列举了一些单输入多输出&#xff08;SIMO&#xff09;和多输入多输出&#xff08;MIMO&#xff09;模型的例子&#xff1a; 单输入多输出&#xff08;SIMO&#xff09;模型&#xff1a; 股票价格预测&#xff1a;在这个例子中&#xff0c;输入可能是某只股票的历史价格数据…

武汉星起航:自运营团队力创佳绩,年流水千万展跨境新高度

在风起云涌的跨境电商领域中&#xff0c;武汉星起航电子商务有限公司凭借其卓越的自运营能力&#xff0c;成为一颗璀璨的明星。而这背后&#xff0c;离不开一支专业性极强、拥有多年自运营经验的团队。他们凭借深厚的行业积累与不懈的拼搏精神&#xff0c;成功在亚马逊平台上打…

《6G数据面架构研究》

目录 一、数据服务的定义二、6G数据服务驱动力及面临的挑战6G数据服务的业务驱动6G数据服务的技术驱动6G数据服务的网络内在驱动6G数据面面临的挑战 三、6G数据服务典型场景自动化网络运维用户体验提升通信感知数据服务 四、6G数据面架构研究数据面架构视图功能定义说明&#x…

比较鸿蒙应用中MVVM与MVP模式在处理数据流、响应用户事件以及职责划分上的异同?

鸿蒙应用中MVVM与MVP模式的异同比较&#xff1a; 数据流处理&#xff1a; MVVM&#xff1a; 数据流从Model流向ViewModel&#xff0c;通过数据绑定机制&#xff08;如ObservableField、LiveData等&#xff09;自动同步到View。ViewModel持有Model的引用&#xff0c;监听数据变…

图深度学习——1.介绍

1、介绍 在数学中&#xff0c;图是描述于一组对象的结构&#xff0c;其中某些对象对在某种意义上是“相关的”&#xff08;存在某种关系&#xff09;。这些对象对应于称为顶点的数学抽象&#xff08;也称为节点或点&#xff09;&#xff0c;并且每个相关的顶点对都称为边&…

视频批量采集下载爬取软件|短视频爬虫提取工具

轻松获取视频&#xff01;视频批量下载神器问世 在日常工作中&#xff0c;我们经常需要大量的视频资源来支持各种需求&#xff0c;但传统的获取方式通常耗时费力&#xff0c;一个一个复制链接下载实在效率太低。为了解决这一难题&#xff0c;我们自主研发了一款强大的短视频批…

分类预测 | Matlab实现KPCA-ISSA-LSSVM基于核主成分分析和改进麻雀优化算法优化最小二乘支持向量机分类预测

分类预测 | Matlab实现KPCA-ISSA-LSSVM基于核主成分分析和改进麻雀优化算法优化最小二乘支持向量机分类预测 目录 分类预测 | Matlab实现KPCA-ISSA-LSSVM基于核主成分分析和改进麻雀优化算法优化最小二乘支持向量机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述…

flex上下固定中间占固定高度(中间左右菜单)且内容自动滚动

效果图 布局&#xff1a; <view class"pop_tSet"><view class"pop_Con"><view class"box_bb"><view class"bb_title">{{titleObj[popType]}}</view></view><view class"box_bb_bot"…

固体矿产资源储量分类GBT17766-2020

1999分类标准采用三轴体系划分资源量与处理&#xff0c;表达复杂、经济意义划分过细、实用性不强 虽然不再采用”三轴“表达方式&#xff0c;但依然考虑地质可靠程度、经济意义、可行性评价 矿产资源勘查&#xff1a;通常依靠地球科学知识&#xff0c;运用地质填图&#xff0…

8条指南教你设计奶油风客厅。福州中宅装饰,福州装修

作为一名专业的设计师&#xff0c;我将为您带来一些关于奶油风客厅设计的干货。奶油风是一种温馨、柔和的装修风格&#xff0c;以下是一些设计指南&#xff0c;帮助您打造一个舒适而美丽的奶油风客厅。 1. 色彩搭配 除了米色、浅黄色和淡粉色等基础色调&#xff0c;还可以尝试…