Linux--进程池
这次,我们来写一个线程池的非常简陋代码,帮助我们理解
我们之前也讲到过:关于C++的池化技术。
进程池是预先创建一组子进程,统一管理任务分配与资源回收的并发模型
什么是进程池?
进程池是预先创建一组空闲进程,统一管理任务队列,避免频繁创建/销毁线程开销的并发编程模型
思路是:
利用父进程管理所有的子进程,而让子进程去完成某种任务!
我们通过匿名管道来实现:
父进程传输信息给子进程,子进程根据父进程传输的信息来执行不同的任务!
简单来说就是:父进程发出指令,由子进程去执行
为什么进程池能够提高效率?
因为OS不相信用户,因此,我们的执行任务的代码中,必定会由大量的系统调用来构成,而上几篇我们讲到,它调用是有成本的!
现在我们采用:预先创建进程,要用时直接拿,而不是需要用到是再去创建进程。同时,在执行完一个任务的子进程还可以继续去执行另外的任务,从而用重复利用的方法提高了资源效率。
我们知道:管理好事物的本质就是:“先描述,在组织!”
创建对象结构体:
class channel
{
public:channel(int comfd, pid_t slaverid, std::string processname): _comdfd(comfd), _slaverid(slaverid), _processname(processname){}// private:
public:int _comdfd; // 发送任务的文件描述符pid_t _slaverid; // 子进程的pidstd::string _processname; // 子进程的名字,方便打日志
};我们操作流程主要有:
初始化
控制进程
关闭进程
int main()
{//模拟执行任务LoadTask(&tasks);//种下随机数种子srand(time(nullptr));// 再组织std::vector<channel> channels;// 初始化,创建多个进程,建立框架!InitSliver(&channels);// Debug(channels);// std::cout<<channels[0]._comdfd<<":"<<channels[0]._slaverid<<":"<<channels[0]._processname<<std::endl;//控制进程ctrlSlaver(channels);QuitSlaver(channels);return 0;
}模拟执行任务:
#pragma once
#include<iostream>
#include<vector>//自定义函数指针
typedef void(*task_t)();void Task1()
{std::cout<<"检查单词是否有问题"<<std::endl;
}void Task2()
{std::cout<<"检查是否要更新版本"<<std::endl;
}void Task3()
{std::cout<<"查看下载进度"<<std::endl;
}void Task4()
{std::cout<<"查看用户是否需要登录"<<std::endl;
}void LoadTask(std::vector<task_t>* tasks)
{tasks->push_back(Task1);tasks->push_back(Task2);tasks->push_back(Task3);tasks->push_back(Task4);进程执行任务
void sliver()
{while(true){int cmdcode=0;//读取到cmdcode中int n=read(0,&cmdcode,sizeof(int));if(n==sizeof(int)){std::cout<<"slaver get a command: "<<getpid()<<" comdcode:"<<cmdcode<<std::endl;if(cmdcode>=0 &&cmdcode<tasks.size()){tasks[cmdcode]();}}if(n==0)break;}
}初始化部分
//输出型参数 *
//输入型参数const &
//输入输出型参数 &
void InitSliver(std::vector<channel>* channels)
{//确保每一个子进程都只有一个写端std::vector<int>oldfd;for (int i = 0; i < 10; i++){int pipefd[2];// 建立管道int n = pipe(pipefd);if (n < 0){perror("pipe");return;}// 创建父子进程pid_t id = fork();if (id < 0){perror("fork");return;}else if (id == 0) // child{for(auto&fd:oldfd)close(fd);close(pipefd[1]); // 关闭一端dup2(pipefd[0], 0);close(pipefd[0]);sliver();std::cout<<"process: "<<getpid()<<" quit"<<std::endl;exit(0);}// fatherclose(pipefd[0]); // 关闭一端std::string processname = "process" + std::to_string(i);channels->push_back({pipefd[1], id, processname});oldfd.push_back(pipefd[1]);// sleep(1);}}简洁菜单:
void Menu()
{std::cout << "####################################################################" << std::endl;std::cout << "####################################################################" << std::endl;std::cout << "#######1.检查单词是否有问题 2.检查是否要更新版本###################" << std::endl;std::cout << "#######3. 查看下载进度 4.查看用户是否需要登录#######################" << std::endl;std::cout << "##########0.退出####################################################" << std::endl;std::cout << "####################################################################" << std::endl;
}
控制进程模块:
void ctrlSlaver(const std::vector<channel>& channels)
{int count=0;while(true){int select=0;Menu();std::cout<<"Please Enter# "<<std::endl;std::cin>>select;if(select<=0 ||select>5)break;//选择任务// int cmdcode=rand()%tasks.size();int cmdcode=select-1;//选择进程int processpos=rand()%tasks.size();std::cout<<"father say:"<<"cmdcode:"<<cmdcode<<" already send to "<<channels[processpos]._slaverid<<" process name: "<<channels[processpos]._processname<<std::endl;//发送任务write(channels[processpos]._comdfd,&cmdcode,sizeof(cmdcode));count++;if(count>=5)break;sleep(1);}}关闭进程
void QuitSlaver(const std::vector<channel>& channels)
{for(const auto e:channels){close(e._comdfd);waitpid(e._slaverid,nullptr,0);}
}注意:关闭进程这里不能写成这样:
for(const auto &c : channels) close(c._cmdfd);for(const auto &c : channels) waitpid(c._slaverid, nullptr, 0);