具体代码在gitee仓库:https://gitee.com/tgwTTT/linux-learning-dai/tree/master/processPool
一、背景与需求
在高并发服务器或批量任务处理场景下,频繁创建和销毁进程会带来较大的系统开销。进程池通过预先创当然可以!下面是一篇更口语化、易读的博客,结合你的代码讲解进程池的原理和实现。
设计思路
1.channel和channelManager
每个子进程和主进程之间用管道通信。channel 类就是管道的“信使”,负责把任务码发给子进程。channelManager 就像个调度员,管理所有信道,轮流分配任务,保证每个子进程都有活儿干。
2. processPool
processPool 负责创建子进程、分发任务、回收资源。主进程用 Run() 方法循环分发任务,子进程用 Work() 方法接收任务码,然后执行对应的操作。
3. TaskManager
这个类负责注册各种任务,比如打印日志、下载、上传、删除、压缩。主进程随机生成任务码,分发给子进程,子进程收到码后就去执行。
代码拆解
创建进程池
主进程会循环创建管道和子进程。每个子进程只保留管道的读端,主进程只保留写端。这样可以避免文件描述符乱用。
for(int i=0;i<_process_num;i++){
int pipefd[2]={0};
pipe(pipefd);
pid_t subid=fork();
if(subid==0){
close(pipefd[1]);
Work(pipefd[0]);
close(pipefd[0]);
exit(0);
}else{
close(pipefd[0]);
_cm.Insert(pipefd[1],subid);
}
}
主进程分发任务
主进程每隔一秒分发一个任务码,轮流给各个子进程。分发十次后就退出。
int cnt=0;
while(true){
if(cnt++>10){
stop();
break;
}
int taskcode=_tm.Code();
channel &c=_cm.Select();
c.Send(taskcode);
sleep(1);
}
子进程接收任务
子进程不断从管道读任务码,收到就执行对应任务。主进程关闭管道后,子进程就退出。
while(true){
int code=0;
ssize_t n=read(rfd,&code,sizeof(code));
if(n>0){
_tm.Execute(code);
}else if(n==0){
break;
}else{
break;
}
}
资源回收
主进程退出前会关闭所有管道写端,并回收所有子进程,避免僵尸进程。
void stop(){
_cm.stopsubprocess();
_cm.waitsubprocess();
}
三、关键实现细节
管道通信
主进程只保留管道写端,子进程只保留读端,避免文件描述符泄漏和误用。
进程创建与分离
fork() 后,子进程执行 Work() 并在结束后 exit(0),防止子进程继续执行主进程逻辑。
资源回收
主进程在退出前关闭所有管道写端,并通过 waitpid 回收所有子进程,避免僵尸进程。
错误处理
管道写入失败时输出错误信息,便于定位问题。
运行截图:

今天的更新就到这里了,如有错误欢迎评论区指出!!
评论
还没有任何评论,你来说两句吧!