简易进程池的实现

什么是进程池?

        进程池(Process Pool)是一种用于管理和复用多个进程的技术或设计模式。在进程池中,一定数量的进程会被预先创建并保持在内存中,以便在需要时立即使用,而不是每次需要进程时都重新创建新的进程,这样可以提高系统的性能和效率。

        进程池通常用于需要频繁创建和销毁进程的场景,例如网络服务器等。通过预先创建一些进程并保持它们处于空闲状态,可以避免频繁创建和销毁进程所带来的开销,并且可以更好地控制同时进行的进程数量,以避免系统资源被耗尽。

        一般来说,进程池包括以下几个基本组件:
1. 进程池管理器(Process Pool Manager):负责创建、管理和维护进程池中的进程,包括池中进程的初始化、分配和回收等操作。
2. 进程队列(Process Queue):用于存放空闲进程的队列,当有任务需要处理时,可以从队列中取出一个空闲进程进行任务处理。
3. 任务队列(Task Queue):用于存放需要处理的任务,当一个进程空闲时,可以从任务队列中取出一个任务进行处理。
4. 进程间通信机制:用于进程之间的通信,例如管道、共享内存、消息队列等。

通过合理设计和使用进程池,可以提高系统的并发处理能力,降低系统资源消耗,同时也便于监控和管理进程。

以下是我们的简易进程池的框架。 

 

因此在本次项目中,在面向对象思想的指导下,我们需要创建一个进程池,管理进程的相关操作。

思路 

我们在写之前,首先需要明确项目的功能是什么?都需要实现哪些模块?

将大框架搭建好之后,逐步填充细节。

首先我们明确需要实现的功能是:一个父进程开辟进程池中的多个子进程,然后向子进程发送任务信息,由子进程执行任务。

实现的模块:进程池(包含子进程的创建,子进程的执行任务板块,子进程的销毁),任务模块,父进程控制块。

我们可以将进程封装为一个小类,再用进程池封装进程的类。利用匿名管道的特性实现父子进程间通信。 

需要注意进程与任务间的负载均衡。

一个超级大Bug 

我们知道,子进程是会继承父进程的文件信息列表的,因此当父进程以写端打开管道,其后创建的子进程将会继承当前父进程的所有wfd与rfd,但由于父进程rfd个数为0,但wfd会叠加,因此最后一个子进程将会继承前面父进程的所有wfd。也就是说,后面创建的进程,会保存前面创建的管道的写文件描述符。 因此倘若我们按从前往后的顺序关闭父进程写端同时进行wait等待,是没有结果的。

我们的解决方法是每创建一个子进程,都会关闭其从父进程那里继承来的所有写文件描述符。

当然也有别的办法,1.从后往前关闭管道,最后的管道只有父进程一个写端。

2. 将所有的写端全部结束之后再进行wait等待。

源码 

task.hpp 

任务模块,其内包含任务列表,与工作过程 

#pragma once
#include<iostream>
#include<unistd.h>
using namespace std;typedef void (*work_t)(int);//函数指针类型
typedef void(*task_t)();void task1()
{cout<<"i'm task11111, hello people! from sub process: "<<getpid()<<endl;
}void task2()
{cout<<"i'm task22222, hello people! from sub process: "<<getpid()<<endl;
}void task3()
{cout<<"i'm task33333, hello people! from sub process: "<<getpid()<<endl;
}//任务的函数指针数组,存储任务列表
task_t taskarray[]={task1,task2,task3};//寻找下一个派发的任务
int NextTask()
{//随机抽取任务return rand()%3;
}//工作过程
void worker(int rfd)
{while(true){sleep(1);int taskcode=0;//接收任务码int n=read(rfd,&taskcode,sizeof(taskcode));//当可以读取到任务信息执行任务if(n==sizeof(taskcode)){//执行任务taskarray[taskcode]();cout<<"task success excute... processid: "<<getpid()<<endl<<endl;}else//读取不到任务信息即退出{cout<<"no task can excute,exit...  processid: "<<getpid()<<endl<<endl;break;}}}

processpool.cc 

完成进程池的创建,销毁与回收等待,同时保证任务的正确执行与退出。 

#include<iostream>
#include<string>
#include<vector>
#include<sys/types.h>
#include<sys/wait.h>
#include<unistd.h>
#include"task.hpp"
using namespace std;//管理管道属性
class channel
{
public:channel(size_t wfd,size_t pid,string name):_wfd(wfd),_process_id(pid),_channel_name(name){}size_t wfd(){return _wfd;}size_t pid(){return _process_id;}string name(){return _channel_name;}void Close(){close(_wfd);}~channel(){}private:size_t _wfd;size_t _process_id;string _channel_name;
};//管理进程池
class processpool
{
public:processpool(int sub_process_num):_sub_process_num(sub_process_num){}//创建进程池void CreatProcessPool(work_t worker){for(int i=0;i<_sub_process_num;i++)//创建管道与进程{int pipefd[2];pipe(pipefd);pid_t pid=fork();vector<int> fds;//存放管道中除却父进程以外的写端,并在子进程中一一进行释放if(pid==0)//子进程读{close(pipefd[1]);for(int i=0;i<fds.size();i++){close(fds[i]);}//子进程接收父进程发送的任务并完成任务worker(pipefd[0]);exit(0);}//父进程为写端close(pipefd[0]);string name="channel-";name+=to_string(pid);_channels.push_back(channel(pipefd[1],pid,name));fds.push_back(pipefd[1]);//将父进程的wfd进行插入,当下一个子进程创建后会继承该文件信息//因此在子进程中需要关闭继承到的写端,以免管道出现多个写端的状况//多个写端造成后果,当父进程终止写入,管道仍旧有多个管道//父进程发送任务给子进程}}void PrintDebug()//打印进程池中进程相关信息{for(auto &e: _channels){cout<<"_wfd: "<<e.wfd()<<"\t";cout<<"_process_pid: "<<e.pid()<<"\t";cout<<"_channel_name: "<<e.name()<<"\t";cout<<endl;}}//寻找下一个分配任务的子进程int NextChannel(){static int cnt=0;int ret=cnt%_channels.size();cnt++;return ret;}//发送任务信息码给子进程void SendTaskMessage(int index,int taskcode){cout<<"taskcode:"<<taskcode<<"  channel id: "<<_channels[index].pid()<<endl;int n=write(_channels[index].wfd(),&taskcode,sizeof(taskcode));}//杀死进程池中所有子进程void KillAll(){for(int i=0;i<_channels.size();i++){_channels[i].Close();}}//对所有子进程进行回收void Wait(){for(int i=0;i<_channels.size();i++){pid_t pid=_channels[i].pid();int ret=waitpid(pid,nullptr,0);if(ret=pid)cout<<"sub process already recyle success... processid: "<<pid<<endl;elsecout<<"sub process already recyle fail fail fail!!!  processid: "<<pid<<endl;}}
private:int _sub_process_num;vector<channel> _channels;
};//控制进程池执行任务
void CtrlProcessPool(processpool Processpool,int cnt)
{while(cnt-->0){//挑选进程int index=Processpool.NextChannel();//挑选任务int taskcode=NextTask();//发送任务给进程sleep(1);cout<<"第"<<cnt<<"个任务"<<endl;Processpool.SendTaskMessage(index,taskcode);}
}
int main(int argc,char* argv[])
{if(argc!=2)//规范启动法则{cout<<"Please Re-Enter!  Please enter subprocess numbers!"<<endl;return -1;}//启动成功int subprocess_num=stoi(argv[1]);//创建进程池processpool Processpool(subprocess_num);Processpool.CreatProcessPool(worker);//Processpool.PrintDebug();//控制子进程//挑选进程与任务,并将任务发送给对应进程CtrlProcessPool(Processpool,7);//结束后回收子进程//关闭写端,进而关闭子进程Processpool.KillAll();//父进程等待回收子进程Processpool.Wait();return 0;
}

代码中有详细注释。 

运行结果 

 

这里的任务与进程是整数倍的关系,因此显得比较规整。 

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

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

相关文章

基于Qt的社团信息管理系统

企鹅2583550535 项目和论文都有 第1章 绪论... 1 1.1 研究背景与意义... 1 1.2 国内外发展现状... 2 1.3 研究内容... 3 第2章 关键技术介绍... 4 2.1 主要开发技术... 4 2.1.1 C. 4 2.1.2 QT框架... 4 2.1.3 MySQL数据库... 5 2.1.4 TCP协议... 6 2.2 其他技术介绍.…

GPIO模拟spi时序点亮数码管

目录 spi.h spi.c main.c 实验效果 spi.h #ifndef __SPI_H__ #define __SPI_H__#include "stm32mp1xx_gpio.h" #include "stm32mp1xx_rcc.h"//spi初始化 void spi_init(); //spi写入数据 void spi_write(unsigned char data);#endif spi.c #include…

Andoird使用Room实现持久化及使用Room进行增删查改

文章目录 Room概述Room的使用一、在gradle.build中添加依赖库kotlinJava 创建实体类创建抽象Dao层接口创建DataBase层使用创建的查看数据库 总结&#xff1a; 这篇文章会告诉你如何在Android中通过kotlin或者Java来实现数据持久化 Room概述 处理大量结构化数据的应用可极大地受…

深入了解Socket套接字

目录 一、引入&#x1f64c; 1、概念 &#x1f389; 2、分类&#x1f389; Socket 套接字主要针对传输层协议分为流套接字、数据报套接字、原始套接字&#xff08;了解即可&#xff09;三类。 1&#xff09;流套接字&#xff1a;使用传输层TCP协议 2&#xff09;数据报套…

温故而知新-MySQL篇【面试复习】

温故而知新-数据库篇【面试复习】 前言版权推荐温故而知新-Mysql篇Mysql常见面试题Mysql事务Mysql索引Mysql锁Mysql日志Mysql中的Buffer 数据库的三范式是什么MySQL对于LRU的优化InnoDB三大特性自适应哈希索引&#xff08;Adaptive Hash Index&#xff09;插入缓存&#xff08;…

基于51单片机的盆栽自动浇花系统

一.硬件方案 工作原理是湿度传感器将采集到的数据直接传送到ADC0832的IN端作为输入的模拟信号。选用湿度传感器和AD转换&#xff0c;电路内部包含有湿度采集、AD转换、单片机译码显示等功能。单片机需要采集数据时&#xff0c;发出指令启动A/D转换器工作&#xff0c;ADC0832根…

HLS入门

文章目录 一HLS是什么1HLS介绍 二HLS核心技术以及技术局限性1HLS的核心技术2HLS的技术局限性 三HLS的LED流水灯1创建项目工程2代码3仿真 一HLS是什么 1HLS介绍 HLS是一种将高级编程语言&#xff08;如 C、C等&#xff09;描述的算法或逻辑自动转换为 FPGA 可实现的硬件描述语…

计算机网络学习小结_物理层

数据通信基础知识 信道相关概念 单工&#xff0c;半双工&#xff0c;全双工 基带信号&#xff1a;信源发出的信号&#xff0c;如计算机输出的文字和图像都是基带信号。基带信号常包含较多低频成分&#xff0c;有的还有直流成分&#xff0c;有的信道不能传输低频成分和直流成…

数据结构--顺序表

目录 1.顺序表 1.1顺序表的概念及结构 线性表 2、顺序表分类 2.1顺序表和数组的区别 静态顺序表 动态顺序表 3.顺序表的实现 3.1初始化 随后便可对顺序表初始化 3.2插入数据 尾插 头插 在指定位置插入数据 顺序表的查找 头删、尾删及指定位置删除 实现代码&#x…

汽车R155法规中,汽车获取到的VTA证书,E后面的数字表示什么意思?

标签&#xff1a; 汽车R155法规中&#xff0c;汽车获取到的VTA证书&#xff0c;E后面的数字表示什么意思&#xff1f;&#xff1b; 汽车&#xff1b;VTA认证; 有些厂商汽车拿到的VTA证书上面写着E9&#xff0c; 有些厂商汽车拿到的VTA证书上面写着E5&#xff0c;E9与E5有什么差…

primeflex样式库笔记 Display相关的案例

回顾 宽度设置的基本总结 w-full&#xff1a;表示widtdh&#xff1a;100%&#xff1b;占满父容器的宽度。 w-screen&#xff1a;表示占满整个屏幕的宽度。 w-1到w-12&#xff0c;是按百分比划分宽度&#xff0c;数字越大&#xff0c;占据的比例就越大。 w-1rem到w-30rem&…

Vitis HLS 学习笔记--控制驱动任务示例

目录 1. 简介 2. 代码解析 2.1 kernel 代码回顾 2.2 功能分析 2.3 查看综合报告 2.4 查看 Schedule Viewer 2.5 查看 Dataflow Viewer 3. Vitis IDE的关键设置 3.1 加载数据文件 3.2 设置 Flow Target 3.3 配置 fifo 深度 4. 总结 1. 简介 本文对《Vitis HLS 学习…

零拷贝(Zero-Copy)

1.背景 现在有这样一个场景&#xff0c;我们需要在本地选择一个文件后&#xff0c;然后上传到网络上。 我们再看看文件的内容数据的具体搬运过程&#xff1a; 你会发现&#xff0c;在整个文件搬运的过程中&#xff0c;发生了多次的数据拷贝和上下文转换。 4次数据拷贝&#…

Git总结超全版

最近想系统的回顾一下Git的使用&#xff0c;如果只想快速的集成git到idea&#xff0c;可以参考另一篇我的博客中的git部分 目录 版本管理工具简介Git安装与配置Git远程仓库配置 Git常用命令为常用命令配置别名(可选)Git忽略文件.gitignore一些概念*本地仓库操作删除仓库内容 *远…

Wireshark 4.2.5:发现 QUIC 和 VXLAN 协议的新功能

Wireshark 是一种先进且广泛使用的网络协议分析仪&#xff0c;最近发布了新版本 4.2.5&#xff0c;它提供了许多新功能和改进。 Wireshark 4.2.5 发行说明 什么是 Wireshark&#xff1f; Wireshark 是世界上最流行的网络协议分析器。它用于故障排除、分析、开发和教育。 Wiresh…

Elasticsearch 分析器的高级用法一(同义词,高亮搜索)

Elasticsearch 分析器的高级用法一&#xff08;同义词&#xff0c;高亮搜索&#xff09; 同义词简介分析使用同义词案例 高亮搜索高亮搜索策略unifiedplainvh 同义词 简介 在搜索场景中&#xff0c;同义词用来处理不同的查询词&#xff0c;有可能是想表达相同的搜索目标。 例…

系统架构师考试(九)

TCP/IP协议族 SMTP是简单邮件传输协议 DNS 域名解析协议 URL - IP&#xff0c;通过URL解析ip是哪一台电脑 DHCP 动态IP地址分配的协议 SNMP 简单网络管理协议 TFTP 简单文件管理协议 ICMP 是网络中差错校验&#xff0c;差错报错的协议 IGMP G是组&#xff0c;组…

Hadoop3:客户端向HDFS写数据流的流程讲解(较枯燥)

一、场景描述 我们登陆HDFS的web端&#xff0c;上传一个大文件。 二、流程图 三、讲解 流程1&#xff08;Client与NameNode交互&#xff09; 1、HDFS client创建DistributedFileSystem&#xff0c;通过dfs与NameNode进行2次&#xff08;一来一回4次&#xff09;对话&#x…

了解K8s集群kubectl命令进行陈述式资源管理

前言 在 Kubernetes 集群中&#xff0c;通过陈述式和声明式资源管理是确保应用程序高效运行的关键。认识这两种管理方法&#xff0c;能够更好地掌握 Kubernetes 集群的运维和管理。 目录 一、K8s 资源管理操作分类 1. 陈述式 2. 声明式 3. K8s 集群管理常用命令概览 二…

Docker Desktop安装和如何在WSL2中使用Docker

最近在使用WSL的过程中&#xff0c;想使用docker遇到了一些问题&#xff0c;在WSL中安装Linux版本的docker&#xff0c;启动镜像之后不能从Windows机器的端口映射出来&#xff0c;查了一圈之后&#xff0c;发现应该使用Docker Desktop软件&#xff0c;下面是安装和使用的方式 …