news 2026/5/2 8:06:10

Linux:简易进程池编写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux:简易进程池编写
  • 设计概念
  • Channel
  • 初始化
  • 创建任务
  • 子进程工作
  • 轮询方案
  • 分配工作
  • 关闭子进程和管道
  • Main

设计概念

进程池,即我们可以预先创建一堆子进程和对应的管道。等父进程有任务时派发给子进程工作。这样就可以节省开辟进程的花销:

当没有任务时,即管道为空。那么子进程就是阻塞状态不会影响其他进程工作效率。

但是注意我们要将任务均衡地派发给子进程,即实现负载均衡

Channel

首先我们要设计一个方案让父进程能统一管理与子进程之间的管道,最好的方法就是封装一个类。
那么类的成员变量必然要有管道的写端fd、子进程的pid还可以有给子进程的编号。

classChannel{public:Channel(intwfd,pid_t id,conststd::string&name):_wfd(wfd),_subprocessid(id),_name(name){}intGetWfd(){return_wfd;}pid_tGetPidProcessId(){return_subprocessid;}std::stringGetName(){return_name;}//关闭写端voidColseChannel(){close(_wfd);}//等待子进程voidWait(){pid_t rid=waitpid(_subprocessid,nullptr,0);if(rid>0){std::cout<<"wait"<<rid<<"success!"<<std::endl;}}~Channel(){}private:int_wfd;pid_t _subprocessid;std::string _name;};

初始化

接下来我们要初始化Channel数组。
根据要创建的worker个数num,我们可以写一个简单的for循环创建管道和子进程。由子进程关闭写端,父进程关闭读端,即可。
但是我们要注意一个细节,这里以创建两个子进程举例,这里先创建第一个worker:

然后分别关闭读写端:

此时我们创建第二个worker:

然后分别关闭读写端:

这时我们发现第二个子进程对第一个管道的写端并没有关闭,此时有可能造成父进程最后无法结束进程。
因此我们在创建新的worker时,要将前面的worker的写端关闭:

voidCreatChannelAndSub(intnum,std::vector<Channel>*channels,task_t task){for(inti=0;i<num;i++){intpipefd[2]={0};intn=pipe(pipefd);//创建管道失败if(n<0)exit(1);//创建子进程pid_t id=fork();if(id==0){//非空就要关闭前面的写端if(!channels->empty()){for(auto&channel:*channels)channel.ColseChannel();}close(pipefd[1]);//重定向到标准输入dup2(pipefd[0],0);//回调函数task();close(pipefd[0]);exit(0);}//父进程std::string channel_name="Channel-"+std::to_string(i);close(pipefd[0]);channels->emplace_back(pipefd[1],id,channel_name);}}

创建任务

我们来实现不同的任务以分配给子进程,首先重命名下函数指针用以实现回调函数:

随意实现三个简单的任务:

voidPrint(){std::cout<<"I am print task"<<std::endl;}voidDownLoad(){std::cout<<"I am download task"<<std::endl;}voidFlush(){std::cout<<"I am flush task"<<std::endl;}

创建回调表:

分配任务:

intSelectTask(){returnrand()%TaskNum;}

执行任务:

voidExcuteTask(intnumber){if(number<0||number>2)return;tasks[number];}

子进程工作

以及有了上面的一系列任务,我们是时候给子进程工作了:

voidwork(){while(true){intcommand=0;intn=read(0,&command,sizeof(command));if(n==sizeof(int)){std::cout<<"pid is:"<<getpid()<<"handler task"<<std::endl;ExcuteTask(command);}//写端关闭if(n==0){std::cout<<"sub process:"<<getpid()<<" quit"<<std::endl;break;}}}

轮询方案

前面提到我们要实现负载均衡,这里可以简单实现为轮询。即轮流给子进程派送任务:

intNextChannel(intchannelnum){staticintnext=0;intchannel=next;next++;next%=channelnum;returnnext;}

分配工作

子进程的工作也有了,轮询方案也有了,就可以给子进程正是分配工作了:

voidSendTaskCommand(Channel&channel,inttaskcommand){write(channel.GetWfd(),&taskcommand,sizeof(int));}voidctrlProcessOnce(std::vector<Channel>&channels){sleep(1);//挑选任务inttaskcommand=SelectTask();//挑选信道和进程intchannel_index=NextChannel(channels.size());//发送任务SendTaskCommand(channels[channel_index],taskcommand);std::cout<<std::endl;std::cout<<"taskcommand:"<<taskcommand<<"channel:"<<channels[channel_index].GetName()\<<"sub process:"<<channels[channel_index].GetPidProcessId()<<std::endl;}voidctrlProcess(std::vector<Channel>&channels,inttimes=-1){if(times>0){while(times--)ctrlProcessOnce(channels);}else{while(true)ctrlProcessOnce(channels);}}

关闭子进程和管道

有了前面关闭子进程所有写端的处理,我们能直接关闭子进程的读端进而使子进程退出,对子进程等待即可:

voidCleanUpChannel(std::vector<Channel>&channels){for(auto&channel:channels){channel.ColseChannel();channel.Wait();}}

Main

接下来就是在main函数里安排代码执行的顺序,我们还可以通过选项的形式控制生成的子进程数量:

intmain(intargc,char*argv[]){if(argc!=2){std::cerr<<"Usage:"<<argv[0]<<" processnum"<<std::endl;}intnum=std::stoi(argv[1]);std::vector<Channel>channels;//1.创建子进程和信道CreatChannelAndSub(num,&channels,work);//2.控制子进程ctrlProcess(channels,5);//3.回收资源CleanUpChannel(channels);return0;}

来尝试运行代码:

完整代码

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 13:05:30

英雄联盟智能助手League Akari:重新定义游戏体验的完整指南

英雄联盟智能助手League Akari&#xff1a;重新定义游戏体验的完整指南 【免费下载链接】LeagueAkari ✨兴趣使然的&#xff0c;功能全面的英雄联盟工具集。支持战绩查询、自动秒选等功能。基于 LCU API。 项目地址: https://gitcode.com/gh_mirrors/le/LeagueAkari Lea…

作者头像 李华
网站建设 2026/5/1 9:06:59

前端技术的下一次跃迁:从工程化到智能化的全面演进

过去十年&#xff0c;前端技术经历了史无前例的高速演化&#xff1a;从页面切图到组件化框架&#xff0c;从手工构建到高度工程化体系&#xff0c;从简单交互到复杂 Web 应用。如今&#xff0c;我们正站在新一代技术浪潮的门口——前端正在从“工程驱动”迈向“智能驱动”。这一…

作者头像 李华
网站建设 2026/4/25 19:00:32

DownKyi终极指南:快速掌握B站视频下载全技巧

DownKyi终极指南&#xff1a;快速掌握B站视频下载全技巧 【免费下载链接】downkyi 哔哩下载姬downkyi&#xff0c;哔哩哔哩网站视频下载工具&#xff0c;支持批量下载&#xff0c;支持8K、HDR、杜比视界&#xff0c;提供工具箱&#xff08;音视频提取、去水印等&#xff09;。 …

作者头像 李华
网站建设 2026/5/2 4:02:12

Flutter 基础组件实战:Text 与 Container 组件快速上手

一、引言基础的Text和Container组件是 Flutter 界面开发的 “基石”&#xff0c;但仅掌握基础用法不足以应对实际开发中的复杂场景。本文在基础用法之上&#xff0c;补充进阶属性、多场景实战案例、常见问题解决方案&#xff0c;所有代码均可直接复制运行&#xff0c;帮助你从 …

作者头像 李华
网站建设 2026/5/1 5:24:04

17、树莓派:多功能项目与实用资源指南

树莓派:多功能项目与实用资源指南 1. 树莓派人脸识别保险箱 拥有一个保险箱是存放贵重物品的好方法,现在借助免费软件、树莓派和树莓派相机,你可以打造一个 DIY 人脸识别保险箱。免费软件基于 OpenCV,这是一个能在树莓派上良好运行的小程序,相机则使用合适的树莓派相机模…

作者头像 李华
网站建设 2026/5/2 22:28:16

【单片机】orange prime pi开发板与单片机的区别

单片机与Orange Pi的核心差异体现在硬件架构、功能定位和系统支持上&#xff0c;以下是具体区别&#xff1a;一、单片机&#xff08;MCU&#xff09;的特点- 硬件架构&#xff1a;集成CPU、RAM、ROM、I/O接口于单一芯片&#xff0c;资源有限&#xff08;如8位/16位CPU&#xff…

作者头像 李华