cpp11实现线程池(六)——线程池任务返回值类型Result实现
介绍
提交任务函数submitTask中返回的Result类型应该是用Result类包装当前的task,因为出函数之后task即如下形式:return Result(task);
Result和Task都要互相持有对方的指针,Task要将任务执行结果通过Result::setVal(run()) 调用传给其对应Result对象。
Result类的声明和实现如下
// threadpool.h
// 提交到线程池的task任务执行完成后的返回值类型
class Result
{
public:Result(std::shared_ptr<Task> task, bool isValid = true);~Result() = default;//获取任务执行后的返回值void setVal(Any any);// get方法,用户调用这个方法获取task的返回值Any get();
private:Any any_; // 存储任务的返回值Semaphore sem_; // 线程通信std::shared_ptr<Task> task_; // 指向对应获取返回值的任务对象std::atomic_bool isValid_; // 检查返回值是否有效
};// threadpool.cpp
Result::Result(std::shared_ptr<Task> task, bool isValid): isValid_(isValid), task_(task)
{task->setResult(this);
}//获取任务执行后的返回值
void Result::setVal(Any any)
{// 存储task的返回值this->any_ = std::move(any); // Any 类只提供右值拷贝和移动赋值函数sem_.post(); // 获取任务的返回值,增加信号量资源
}// get方法,用户调用这个方法获取task的返回值
Any Result::get()
{if (!isValid_){return "";}sem_.wait(); // task任务如果没执行完,这里会阻塞用户线程return std::move(any_); // Any不提供左值拷贝构造和拷贝赋值
}
Task类的声明和实现如下
// threadpool.h
class Task
{
public:Task();~Task() = default;void exec();void setResult(Result* res);// 用户可自定义任务类型,从Task派生出来,重写run方法,实现自定义任务处理virtual Any run() = 0;private:// 不要用智能指针,可能会导致和Result产生循环引用的问题Result* result_; // Result对象生命周期 > Task对象生命周期
}; // threadpool.cpp
Task::Task(): result_(nullptr)
{}
void Task::exec()
{if (nullptr != result_){result_->setVal(run()); // !!!}}void Task::setResult(Result* res)
{result_ = res;
}
Master-Slave线程模型
Master线程用来分解任务,然后给各个Slave分配任务;
等待各个Slave线程执行完任务,返回结果;
Master线程合并各个任务结果并输出
测试
使用3个线程来计算1+2+…+300000000 的和(1加到3亿)
测试代码如下:
#include <iostream>
#include <chrono>
#include "threadpool.h"using uLong = unsigned long long;class MyTask : public Task
{
public:MyTask(uLong /*int*/ begin, uLong /*int*/ end): begin_(begin), end_(end){}Any run(){std::cout << "tid:" << std::this_thread::get_id() << " begin!" << std::endl;//std::this_thread::sleep_for(std::chrono::seconds(5));uLong sum = 0;for (uLong i = begin_; i <= end_; i++){sum += i;}std::cout << "tid:" << std::this_thread::get_id() << " end!" << std::endl;return sum;}
private:uLong /*int*/ begin_;uLong /*int*/ end_;
};
int main()
{ThreadPool pool;pool.start(4);Result res1 = pool.submitTask(std::make_shared<MyTask>(1, 1000000000));Result res2 = pool.submitTask(std::make_shared<MyTask>(1000000001, 2000000000));Result res3 = pool.submitTask(std::make_shared<MyTask>(2000000001, 3000000000));uLong sum1 = res1.get().cast_<uLong>();uLong sum2 = res2.get().cast_<uLong>();uLong sum3 = res3.get().cast_<uLong>();// Master - Slave线程模型std::cout << (sum1 + sum2 + sum3) << std::endl;getchar();return 0;
}

出现的问题
测试时最后一个获取任务的线程,没有执行完成

而且,似乎出现了死锁的现象,按任意键无反应
分析
根据卡住的位置,我简单的推测是第三个任务执行的问题,于是在提交第三个子任务处打上断点

到达断点后进入MyTask构造函数,在初始化列表发现end_成员被初始化为负数(如下图)

这是由于int类型的最大值在编译器中定义如下
#define INT_MAX 2147483647
小于3000000000,导致了溢出,从而转变成负数。
这样导致在如下的任务执行函数的循环中,程序永远无法出循环

解决办法
将MyTask类中的成员类型改变为unsigned long long
class MyTask : public Task
{
public:MyTask(uLong /*int*/ begin, uLong /*int*/ end): begin_(begin), end_(end){}Any run(){std::cout << "tid:" << std::this_thread::get_id() << " begin!" << std::endl;//std::this_thread::sleep_for(std::chrono::seconds(5));uLong sum = 0;for (uLong i = begin_; i <= end_; i++){sum += i;}std::cout << "tid:" << std::this_thread::get_id() << " end!" << std::endl;return sum;}
private:uLong /*int*/ begin_;uLong /*int*/ end_;
};
程序运行后结果正确
相关文章:
cpp11实现线程池(六)——线程池任务返回值类型Result实现
介绍 提交任务函数submitTask中返回的Result类型应该是用Result类包装当前的task,因为出函数之后task即如下形式:return Result(task); Result和Task都要互相持有对方的指针,Task要将任务执行结果通过Result::setVal(run()) 调用传给其对应…...
道岔外锁闭装置介绍
简述 道岔外锁闭装置是一种能可靠地锁闭尖轨和基本轨的器械。它能有效地克服尖轨在密贴时的转换阻力,即使连接杆折断,外锁闭装置仍在起着锁闭作用。外锁闭能够隔离列车通过时对转换设备的振动和冲击,提高转换设备寿命和可靠性。 产品分类 …...
idea把项目上传到码云
1. 为项目创建仓库 2. 选中中项目右击git, 先add, 在commit Directory 3. 设置远程码云项目地址 4. push项目, ok。 注意: 如果你在最后push出现以下提示,则说明提交失败 Push to origin/master was rejected(译文:推送到原点/master被拒绝…...
设计模式之责任链模式
责任链模式的定义是:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。 责任链模式适合于请求需要经过多个处理器,并…...
Python--我一般都用这个模块压缩文件
打包成压缩文件很多时候都能用上,也包括了自动化中的部分应用。例如,将测试报告打包发送。 本章就来介绍其中一个模块,可以用于结合上一章的内容结合使用。 from zipfile import ZipFile ❝ ZipFile是zipfile的一个方法。 ❞ 提取zip文件 fro…...
Chapter8 :Physical Constraints(ug903)
8.1About Physical Constraints(关于物理约束) XilinxVivado集成设计环境(IDE)允许通过设置对象属性值对设计对象进行物理约束。示例包括: •I/O约束,如位置和I/O标准 •布局约束&…...
星标3.5k,一款国产的轻量级开源在线项目任务管理工具
今天给大家推荐一个轻量级的开源在线项目任务管理工具:DooTask 图片 DooTask 提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM,文件管理等工具。 高效便捷的团队沟通工具 针对项目和任务建立群组,工作问题可…...
【华为OD机试真题2023B卷 JAVA】字符串摘要
华为OD2023(B卷)机试题库全覆盖,刷题指南点这里 字符串摘要 知识点字符串排序 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 给定一个字符串的摘要算法,请输出给定字符串的摘要值。 1、去除字符串中非字母的符号。 2、如果出现连续字符(不区分大小写),则输…...
Java线程概述 (一)线程介绍
文章目录 🐒个人主页🏅JavaSE系列专栏📖前言:🪅什么是程序 、进程、线程?🪅线程的生命周期🪅多线程🪅守护者线程🪅线程并行与并发🪅死锁…...
操作系统第三章——存储系统(下)
锦衣雪华玉颜色,回眸一笑天下倾 文章目录 3.2.1 虚拟内存的基本概念知识总览传统存储方式的特征,缺点局部性原理虚拟内存的定义如何实现虚拟内存技术知识总结 3.2.2 请求分页管理方式知识总览页表机制缺页中断机制地址变换机制知识回顾 3.2.3 页面置换算…...
初识结构体
目录 结构体的声明 结构体的基础知识 结构体的声明 结构体成员的类型 结构体变量的定义和初始化 定义 初始化 结构体成员的访问 结构体变量访问成员 结构体指针访问指向变量的成员 结构体传参 传地址 传结构体 结论 结构体的声明 结构体的基础知识 数组ÿ…...
协程并发下数据汇总:除了互斥锁,还有其他方式吗?
1. 简介 本文介绍了在并发编程中数据汇总的问题,并探讨了在并发环境下使用互斥锁和通道两种方式来保证数据安全性的方法。 首先,通过一个实例,描述了一个并发拉取数据并汇总的案例,并使用互斥锁来确保线程安全。然后,…...
5、Ray-Actor模型和并发编程
5、Ray-Actor模型和并发编程 导航 1.简介和背景 2.Ray的基本概念和核心组件 3.分布式任务调度和依赖管理 4.对象存储和数据共享 5.Actor模型和并发编程 6.Ray的高级功能和扩展性 7.使用Ray构建分布式应用程序的案例研究 8.Ray社区和资源 9.核心框架介绍...
HNU-电路与电子学-小班2
第二次讨论 讨论题目: 1、电子秤的电桥电路可以分别用 1 个压控电阻、 2 个压控电阻、 3 个压控电阻、 4 个压控电阻实现吗?试写出每种实现的 U AB 输出表达式,并分析哪种实现电桥 电压的灵敏度(SV/ △ R )高。 …...
二分图匹配算法
匈牙利算法、Hopcroft-Karp算法和Kuhn-Munkres算法是三种常见的二分图匹配算法,它们在实现方式、时间复杂度和适用场景上有所差异。以下是它们的区别和优缺点: 匈牙利算法: 实现方式:匈牙利算法使用深度优先搜索(DFS)来寻找增广路…...
虹科技术 | 虹科EtherCAT增量编码器输入模块数据采集实操测试
1. 背景介绍 编码器是将信号或数据进行编制、转换为可用以通讯、传输和存储的信号形式的设备。编码器把角位移或直线位移转换成电信号,前者称为码盘,后者称为码尺。按照读出方式编码器可以分为接触式和非接触式两种;按照工作原理编码器可分为…...
2023.05.21 学习周报
文章目录 摘要文献阅读1.题目2.背景3.现存问题和解决方法4.方法4.1 Variational mode decomposition (VMD)4.2 Bidirectional LSTM 5.实验5.1 数据标准化5.2 评价指标5.3 实验过程及结果 6.结论和展望 优劣解距离法有限元1.求解一个简单的传热问题2.有限元如何实现 总结 摘要 …...
资深程序员深度体验ChatGPT一周发现竟然....
周一打卡上班,老板凑到我跟前:“小李啊,这周有个新需求交给你做一下,给我们的API管理平台新增一个智能Mock的功能...”。我条件反射般的差点脱口而出:“这个需求做不了..”。不过在千钧一发之间,我想起了最…...
带你深入了解Android Handler的用法
Android中,Handler是一类用于异步消息传递和线程之间通信的基础框架。一个Handler是一个线程的处理器,可以接收消息,并调度运行它们。使用Handler,应用程序可以将处理器与一个线程关联,以将来的时间运行任务。而使用Ha…...
生于零售的亚马逊云科技,如何加速中国跨境电商企业出海?
导读:跨境电商进入精耕细作的新阶段。 作为中国企业出海的重要领域之一,近几年跨境电商行业处在快速发展中。商务部数据显示,2022年中国跨境电商出口达1.55万亿,同比增长11.7%。2023年1-2月,跨境电商进出口总额同比增长…...
在openclaw中配置taotoken作为默认模型供应商的详细步骤
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 在OpenClaw中配置Taotoken作为默认模型供应商的详细步骤 OpenClaw 是一款功能强大的 AI 智能体开发工具,它允许开发者灵…...
对比直接使用厂商API,Taotoken在稳定性方面的补充价值
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 对比直接使用厂商API,Taotoken在稳定性方面的补充价值 在构建依赖大模型能力的应用时,开发者通常会直接调用…...
零起点Python机器学习快速入门【1.1】
1.4 机器学习经典案例目前人工智能、机器学习正处于黄金时期,各种应用随处可见,以下是一些常见的机器学习应用案例。 机器人客服:当你拨打移动、银行等公司的服务热线时,大部分都是通过人工智能技术合成的电脑客服在和你沟通&am…...
Windows上的安卓应用安装神器:APK-Installer完全指南
Windows上的安卓应用安装神器:APK-Installer完全指南 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 想在Windows电脑上轻松安装安卓应用,又不想…...
从0到1构建DeepSeek企业级隔离体系:4类租户场景×3种SLA等级×2套审计回溯机制
更多请点击: https://intelliparadigm.com 第一章:DeepSeek资源隔离方案的总体架构设计 DeepSeek资源隔离方案以“多租户安全边界 动态资源契约”为核心设计理念,构建覆盖计算、内存、存储与网络四维资源的统一隔离层。该架构采用分层解耦结…...
终极指南:如何快速解密QQ音乐加密音频文件
终极指南:如何快速解密QQ音乐加密音频文件 【免费下载链接】qmc-decoder Fastest & best convert qmc 2 mp3 | flac tools 项目地址: https://gitcode.com/gh_mirrors/qm/qmc-decoder 你是否曾经下载了QQ音乐的歌曲,却发现只能在特定播放器里…...
Android系统级证书安装:解决HTTPS抓包的SSLHandshakeException
1. 为什么系统级证书安装成了Android抓包的“最后一道墙”做Android逆向分析的朋友,大概率都经历过这个场景:Charles或Fiddler配好代理、手机连上Wi-Fi、HTTP流量哗哗地跑,可一打开目标App——空白页、网络错误、甚至直接闪退。点开App的设置…...
张量网络机器学习:从平均风险下界看量子模型泛化极限
1. 项目概述:当张量网络遇见机器学习如果你和我一样,既对量子多体物理中的张量网络着迷,又对机器学习模型的泛化能力充满好奇,那么“张量网络机器学习模型平均风险的理论分析”这个课题,无疑是一个能将两者完美结合的宝…...
DeepSeek-R1、V2、V3如何选?:3分钟掌握版本差异与业务匹配公式
更多请点击: https://kaifayun.com 第一章:DeepSeek-R1、V2、V3如何选?:3分钟掌握版本差异与业务匹配公式 DeepSeek-R1、V2、V3 是 DeepSeek 系列中面向不同推理场景演进的三个关键版本,其核心差异不在参数量堆叠&…...
3步搞定B站m4s转MP4:开源工具让你的缓存视频重获新生
3步搞定B站m4s转MP4:开源工具让你的缓存视频重获新生 【免费下载链接】m4s-converter 一个跨平台小工具,将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾经遇到过这样的烦恼&am…...
