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月,跨境电商进出口总额同比增长…...
深入理解JavaScript设计模式之单例模式
目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式(Singleton Pattern&#…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
搭建DNS域名解析服务器(正向解析资源文件)
正向解析资源文件 1)准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2)服务端安装软件:bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...
高分辨率图像合成归一化流扩展
大家读完觉得有帮助记得关注和点赞!!! 1 摘要 我们提出了STARFlow,一种基于归一化流的可扩展生成模型,它在高分辨率图像合成方面取得了强大的性能。STARFlow的主要构建块是Transformer自回归流(TARFlow&am…...
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章 摘要: 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言,受限于 C 语言本身的内存安全和并发安全问题,开发复杂模块极易引入难以…...
