具体代码在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 回收所有子进程,避免僵尸进程。

错误处理
管道写入失败时输出错误信息,便于定位问题。

运行截图:

今天的更新就到这里了,如有错误欢迎评论区指出!!