进程池是什么呢?我们可以类比内存池的概念来理解进程池。
内存池
内存池是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升。
进程池
进程池是管理进程、资源进程组成的技术应用。进程池技术的应用至少由以下两部分组成:
管理进程:管理进程负责创建资源进程,把任务交给空闲的资源进程处理,回收已经处理完任务的资源进程。
资源进程:预先创建好的空闲进程,管理进程会把任务发送给空闲的资源进程处理。
管理进程要有效的管理资源进程,那么管理进程和资源进程之间必然需要交互,而他们就是通过管道进行信息交互的,还有其他的交互方式等等。
制作一个简单的进程池的思路:我们为了实现两进程之间的通信(具有血缘关系的进程),父进程可以通过向子进程发送任务码来使子进程完成某个任务,传送信息的媒介就是匿名管道。
创建子进程并为每个子进程创建一个与父进程间进行通信的匿名管道。如下:
makefile
ProcessPool:ProcessPool.cc
g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f ProcessPool
ProcessPool.hpp
#pragma once
#include <iostream>
#include <vector>
typedef void(*task)();//重命名函数指针类型
void task1()
{
std::cout<<"更新野区"<<std::endl;
}
void task2()
{
std::cout<<"回复生命值与法力值"<<std::endl;
}
void task3()
{
std::cout<<"英雄升级"<<std::endl;
}
class Task
{
public:
task operator[](int pos)
{
if(pos>=0 && pos<=3)
return tasks[pos];
}
private:
task tasks[4]={nullptr,task1,task2,task3};//函数指针数组
};
ProcessPool.cc
#include "Task.hpp"
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string>
#include <vector>
#include <cstdio>
#include <ctime>
#include <cassert>
#define NUM 2
#define processnum 5
class channel //创建描述管道写端和对应的子进程id的类
{
public:
channel(size_t cmdfd, pid_t slaverid, const std::string& slavername)
:_cmdfd(cmdfd)//用来确定父进程向那个管道发送任务码
,_slaverid(slaverid)//子进程的pid
,_slavername(slavername)//子进程的名字
{}
public:
size_t _cmdfd;
pid_t _slaverid;
std::string _slavername;
};
void Reader()
{
Task t;
while(true)
{
int childtaskcode;
int n = read(0, &childtaskcode, sizeof(childtaskcode));
if(0==n)//如果0==n则说明父进程的写端已关闭或父进程已终止
break;
else if(n==sizeof(int))
t[childtaskcode]();
}
}
void InitProcessPool(std::vector<channel>& channels)
{
std::vector<int> uselesswfd;
for(int i=0;i<processnum;i++)
{
int pipefd[NUM]={0};
int n = pipe(pipefd);
assert(!n);
pid_t id = fork();
//child
if(0 == id)
{
std::cout<<"child process delete uselesswfd:";
for(auto e:uselesswfd)
{
close(e);
std::cout<< e <<" ";
}
std::cout<<std::endl;
close(pipefd[1]);
dup2(pipefd[0], 0);
Reader();
std::cout<<"child exit pid is:"<<getpid()<<std::endl;
exit(0);
}
//parent
uselesswfd.push_back(pipefd[1]);
close(pipefd[0]);
std::string name="process" + std::to_string(i);
channels.push_back(channel(pipefd[1], id, name));//记录每一个管道的写端以及对应子进程pid
}
}
void Menu()
{
std::cout<<"******************************************"<<std::endl;
std::cout<<"*******1.更新野区 2.回复生命值与法力值*****"<<std::endl;
std::cout<<"*******3.英雄升级 0.退出 *****"<<std::endl;
}
void ControlChildProcess(const std::vector<channel>& channels)
{
int whichprocess=0;
srand(time(nullptr));
while(true)
{
sleep(1);
std::cout<<std::endl;
Menu();
int taskcode;
std::cout<<"Please select taskcode:";
std::cin>>taskcode;
if(taskcode<0 || taskcode>3)
{
std::cout<<"找不见对应的任务码!"<<std::endl;
continue;
}
if(0==taskcode)
break;
whichprocess = rand()%channels.size();
write(channels[whichprocess]._cmdfd, &taskcode, sizeof(taskcode));
}
}
void Quitwaitchild(const std::vector<channel>& channels)
{
for(auto& e : channels)
close(e._cmdfd);
for(auto& e : channels)
waitpid(e._slaverid, nullptr,0);
}
int main()
{
//描述并组织管道写端fd以及子进程pid
std::vector<channel> channels;//用于存储管道和其对应的子进程信息
//创建进程池,子进程阻塞等待管道内容
InitProcessPool(channels);
//父进程控制子进程,向子进程发送任务码让子进程执行相应的任务,并间接控制子进程终止
ControlChildProcess(channels);
//终止子进程并且父进程等待回收子进程
Quitwaitchild(channels);
return 0;
}
程序演示如下: