C++异步操作实现线程池

文章目录

    • std::future
      • 用法
        • async函数
        • promise::get_future
        • packaged_task::get_future
    • 线程池实现

std::future

std::future是C++11标准库中的一个模板类,表示异步操作的结果

当我们在进行多线程中使用异步任务时,std::future是用来帮我们在需要的时候获取任务执行的结果

他的一个重要的特性就是能够阻塞当前的线程,一直到异步操作完成,从而确保在获取结果的时候,异步工作线程的任务是完成的

主要应用场景就是异步任务,比如说网络请求或者计算密集型任务;并发控制,在多线程编程中,我们需要等待某些任务完成之后才能继续执行其他操作,实现线程同步;最后就是可以通过std::future::get()来获取任务的结果

用法

async函数
#include <iostream>
#include <thread>
#include <future>
#include "../logs/Xulog.h"int Add(int num1, int num2)
{INFO("正在计算...");return num1 + num2;
}int main()
{// std::async(func, ...)// std::async(policy, func, ...)INFO("异步任务执行");std::future<int> res = std::async(std::launch::deferred, Add, 114, 514);INFO("获取结果");int sum = res.get();INFO("结果是%d", sum);return 0;
}
[24-10-02|16:04:34][139769238738752][root][Async.cc:16][INFO]   异步任务执行
[24-10-02|16:04:34][139769238738752][root][Async.cc:18][INFO]   获取结果
[24-10-02|16:04:34][139769238738752][root][Async.cc:8][INFO]    正在计算...
[24-10-02|16:04:34][139769238738752][root][Async.cc:20][INFO]   结果是628

在我们调用获取结果之后,才显示正在计算,执行异步任务,

这个std::launch::deferred其实就是设置,在获取结果时,才会进行传参调用,deferred本身是推迟的意思

与之对应的就是std::launch::async,是如果有结果的话,直接获取结果,没有的话也是会阻塞等待

promise::get_future

这是一个模板类

请添加图片描述

主要是用来返回future对象,会和future共享一个同步状态

使用样例

#include <iostream>
#include <thread>
#include <future>
#include <chrono>
#include "../logs/Xulog.h"void Add(int num1, int num2, std::promise<int> &prom)
{INFO("正在计算...");prom.set_value(num1+num2);
}
int main()
{std::promise<int> prom; // 创建promise对象std::future<int> fu = prom.get_future(); // 关联future对象std::thread thr(Add, 114, 514, std::ref(prom));// 异步线程中对promise对象进行设置值int res = fu.get();// 从future中获取数据INFO("结果是%d", res);thr.join();return 0;
}

这里面是隐含了一个阻塞问题,在promise和future中是同步的,自动进行同步

一定是先运行有了结果,才能获取

packaged_task::get_future

这是将一个函数封装起来,也可以返回一个封装对象,来获取他保存的这个函数的执行结果

请添加图片描述请添加图片描述

演示是这样的

#include <iostream>
#include <thread>
#include <future>
#include <memory>
#include "../logs/Xulog.h"int Add(int num1, int num2)
{INFO("正在计算...");return num1 + num2;
}int main()
{// std::packaged_task<int(int, int)> task(Add); // 包装函数// std::future<int> fu = task.get_future();     // 设置关联// 这样是不行的// std::async(std::launch::async, task, 114, 514);// std::thread(task, 114, 514);// task(11, 22); // 执行// std::shared_ptr<std::packaged_task<int(int, int)>>auto ptask = std::make_shared<std::packaged_task<int(int, int)>>(Add);std::future<int> fu = ptask->get_future();     // 设置关联std::thread thr([ptask](){ (*ptask)(114, 514); });int sum = fu.get();INFO("结果是%d", sum);thr.join();return 0;
}

他可以使用可调用对象的使用,但又不能完全当作函数去使用

但是也可以曲线救国,让task封装在指针里,传给异步线程

如果单纯使用指针,存在生命周期的问题,有可能出现风险

我们就可以在堆上new对象,用智能指针来管理

线程池实现

std::packaged_task+std::future

使用方法:用户传入要执行的函数和参数,由线程池中的工作线程完成任务

实现:

  • 管理的成员
    • 任务池:用vector维护的一个函数池
    • 互斥锁&条件变量:同步互斥
    • 工作线程:从任务池取出任务执行任务
    • 结束允许标志:控制线程池结束
  • 管理的操作
    • 入队任务:入队一个函数和参数
    • 停止运行:终止线程池
#include <iostream>
#include <functional>
#include <memory>
#include <thread>
#include <future>
#include <mutex>
#include <vector>
#include <condition_variable>
#include <atomic>
#include "../logs/Xulog.h"class threadpool
{
public:using Functor = std::function<void(void)>;threadpool(int thr_count = 1) : _stop(false){for (int i = 0; i < thr_count; i++)_threads.emplace_back(&threadpool::entry, this);}~threadpool(){stop();}void stop(){if (_stop == true)return;_stop = true;_cv.notify_all(); // 唤醒线程for (auto &thread : _threads)thread.join();}// 自动推导返回值类型template <typename F, typename... Args>auto push(F &&func, Args &&...args) -> std::future<decltype(func(args...))>{// 将传入函数封装成packaged_task任务包using return_type = decltype(func(args...));auto tmp_func = std::bind(std::forward<F>(func), std::forward<Args>(args)...);auto task = std::make_shared<std::packaged_task<return_type()>>(tmp_func);std::future<return_type> fu = task->get_future();// 构造lambda表达式(捕获任务对象,函数内执行任务对象){std::unique_lock<std::mutex> lock(_mutex);// 将构造出来的匿名对象传入任务池_taskpool.push_back([task](){ (*task)(); });_cv.notify_one();}return fu;}private:// 线程入口函数 从任务池中取出任务执行void entry(){while (!_stop){// 临时任务池// 避免频繁加解锁std::vector<Functor> tmp_taskpool;{// 加锁std::unique_lock<std::mutex> lock(_mutex);// 等待任务不为空或_stop被置为1_cv.wait(lock, [this](){ return _stop || !_taskpool.empty(); });// 取出任务进行执行tmp_taskpool.swap(_taskpool);}for (auto &task : tmp_taskpool){task();}}}private:std::atomic<bool> _stop;std::vector<Functor> _taskpool;std::mutex _mutex;std::condition_variable _cv;std::vector<std::thread> _threads;
};int Add(int num1, int num2)
{return num1 + num2;
}int main()
{threadpool pool;for (int i = 0; i < 10; i++){std::future<int> fu = pool.push(Add, 114, 514 + i);INFO("结果是%d", fu.get());}pool.stop();return 0;
}

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

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

相关文章

基于spring boot的篮球论坛系统

作者&#xff1a;计算机搬砖家 开发技术&#xff1a;SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;Java精选实战项…

快停止这种使用U盘的行为!

前言 现在各行各业的小伙伴基本上都需要用电脑来办公了&#xff0c;你敢说你不需要用电脑办公&#xff1f; 啊哈哈哈&#xff0c;用iPad或者手机办公的也算。 有些小伙伴可能经常996&#xff0c;甚至有时候都是007。有时候到了下班时间&#xff0c;工作还没做完&#xff0c;…

图文深入理解Oracle DB企业级集中管理神器-GC的安装和部署

值此国庆佳节&#xff0c;深宅家中&#xff0c;闲来无事&#xff0c;就多写几篇博文。今天继续宅继续写。 本文承接上篇&#xff0c;介绍GC的安装和部署。咱们不急&#xff0c;慢慢来&#xff0c;饭要一口一口地吃才能吃得踏实自然。 限于篇幅&#xff0c;本节将重点介绍关键步…

VRRP协议个人理解+报文示例+典型配置-RFC2338/RFC3768/RFC5798/RFC9568

个人认为&#xff0c;理解报文就理解了协议。通过报文中的字段可以理解协议在交互过程中相关传递的信息&#xff0c;更加便于理解协议。 因此本文将在VRRP协议报文的基础上进行介绍。 VRRP协议发展 关于VRRPv2基本原理&#xff0c;可重点参考2004年发布的RFC3768-Virtual Ro…

深入理解NumPy库:常用函数详解与数组操作指南

在数据科学和数值计算领域&#xff0c;NumPy无疑是一个强大的工具&#xff0c;它为Python提供了高效的多维数 组处理能力。无论是进行数据分析、构建机器学习模型&#xff0c;还是进行复杂的科学计算&#xff0c;NumPy都是 不可或缺的核心库之一。 numpy.array 是 NumPy 库中…

UART通信—基于江科大源码基础进行的改进和解析

我就不讲理论了&#xff0c;CSDN上大佬属实多&#xff0c;我就只讲代码了&#xff0c;串口的基本理论&#xff0c;大家去看其他大佬写的吧 一、源文件的组成 1、包含的头文件 stm32f10x.h 是STM32F10x系列微控制器的标准外设库&#xff08;Standard Peripheral Library&…

【算法业务】数据驱动的用户增长实践、收益及思考

这篇内容是多年之前&#xff08;2020年&#xff09;的用户增长项目时自己写的总结&#xff0c;这里做一下对于实践和思考的回顾&#xff0c;便于知识的记录和经验分享&#xff0c;内容涉及用户增长理解、个性化推送系统框架、个性化推送问题建模、推送内容池构建、智能文案生成…

zotero使用koofr+google drive/onedrive同步

最早了解到这个思路是来自 知乎-【从零开始使用Zotero】(3) Zotero文献同步方式 和 How to Sync Zotero Files Using WebDAV and Google Drive with Koofr: A Step-by-Step Guide 虽然周围很多人都在用Readpaper这种web端的了&#xff0c;但是经常逛Arxiv而且zotero的web插…

MATLAB|电气互联系统有功-无功协同优化模型

目录 1 主要内容 模型示意图 目标函数 程序亮点 2 部分程序 3 程序结果 4 下载链接 1 主要内容 本程序基本复现《“碳中和”目标下电气互联系统有功-无功协同优化模型》&#xff0c;文献模型提供了一个很好的创新思路&#xff0c;把常规电气互联系统的调度和有功无功优化…

Android Framework AMS(01)AMS启动及相关初始化1-4

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要涉及systemserver启动AMS及初始化AMS相关操作。同时由于该部分内容分析过多&#xff0c;因此拆成2个章节&#xff0c;本章节是第一章节&…

10.4学习

1.Transactional 注意事项&#xff1a; ①事务函数中不要处理耗时任务&#xff0c;会导致长期占有数据库连接。 ②事务函数中不要处理无关业务&#xff0c;防止产生异常导致事务回滚。 ●事务传播属性 ①REQUIRED&#xff08;默认属性&#xff09; 如果存在一个事务&#…

基于Springboot+VUE的二手奢侈品商城的设计与实现

一、摘要 当前&#xff0c;二手奢侈品市场持续蓬勃发展&#xff0c;吸引了越来越多的消费者。然而&#xff0c;现有的二手奢侈品交易平台在用户体验、安全性和功能方面仍存在一些问题&#xff0c;需要进一步改进。本研究旨在设计和实现一种基于Spring Boot 和 Vue 技术框架的二…

17 链表——21. 合并两个有序链表 ★

17 链表 21. 合并两个有序链表 将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 = [1,2,4], l2 = [1,3,4] 输出:[1,1,2,3,4,4] 算法设计: 合并两个有序链表,并保持有序性,可以采用迭代法和递归法两种…

Unity代码组件,代码控制旋转+RotateAround、Translate,LookAt相关

创建脚本 在Project面板创建一个Scripts文件夹&#xff0c;用于专门存放脚本&#xff0c;创建一个脚本&#xff0c;命名为RotationControl,将该脚本拖拽到Tank上&#xff0c;则Tank物体成功挂载该脚本。 双击打开脚本&#xff0c;编写脚本如下&#xff1a; using System.Coll…

国外电商系统开发-运维系统批量添加服务器

您可以把您准备的txt文件&#xff0c;安装要求的格式&#xff0c;复制粘贴到里面就可以了。注意格式&#xff01; 如果是“#” 开头的&#xff0c;则表示注释&#xff01;

746. 使用最小花费爬楼梯

文章目录 746. 使用最小花费爬楼梯思路总结 746. 使用最小花费爬楼梯 746. 使用最小花费爬楼梯 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标…

Java之队列

1. 概念 队列&#xff1a;只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性 特点&#xff1a; 队列具有先进先出FIFO(First In First Out) 入队列&#xff1a;进行插入操作的一端称为队尾&#xff08;Tail/Rear&#xff09; 出队列&#xff1a;进…

【论文笔记】DKTNet: Dual-Key Transformer Network for small object detection

【引用格式】&#xff1a;Xu S, Gu J, Hua Y, et al. Dktnet: dual-key transformer network for small object detection[J]. Neurocomputing, 2023, 525: 29-41. 【网址】&#xff1a;https://cczuyiliu.github.io/pdf/DKTNet%20Dual-Key%20Transformer%20Network%20for%20s…

等额本金和等额本息是什么意思?

等额本金和等额本息是两种常见的贷款还款方式&#xff0c;它们各自有着不同的特点和适用场景。下面我将用通俗易懂的语言来解释这两种还款方式&#xff1a; 等额本金 定义&#xff1a;等额本金指的是在贷款期限内&#xff0c;每月偿还相同数额的本金&#xff0c;而利息则随着剩…

buuctf[安洵杯 2019]easy misc1

解压的一个文件夹和图片一个&#xff0c;zip压缩包有密码 FLAG IN ((√2524921X8552)15-1794)NNULLULL, ((√2524921X8552)15-1794)7 我用passware kit 2022 所以试试7位数字NNULLULL,掩码&#xff08;mask&#xff09;攻击试试 mask &#xff1a;?d?d?d?d?d?d?dNNUL…