首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【Linux】实现一个简易的进程池

【Linux】实现一个简易的进程池

作者头像
修修修也
发布2024-11-21 13:00:48
发布2024-11-21 13:00:48
18600
代码可运行
举报
运行总次数:0
代码可运行

池化技术

在之前学习STL的时候我们曾经了解过内存池的概念,今天我们要实现的进程池和内存池一样,都是一种池化技术:

池化技术 (Pool) 是一种很常见的编程技巧,在请求量大时能明显优化应用性能,降低系统频繁建连的资源开销。我们日常工作中常见的有数据库连接池、线程池、对象池等,它们的特点都是将 “昂贵的”、“费时的” 的资源维护在一个特定的 “池子” 中,规定其最小连接数、最大连接数、阻塞队列等配置,方便进行统一管理和复用,通常还会附带一些探活机制、强制回收、监控一类的配套功能。


进程池的设计原理

我们一次性创建10个子进程,然后通过一个vector将它们组织起来管理,再建立10个相应的管道和它们通信,为这10个进程发布任务,其底层关系如下图所示(虚线表示创建过联系但不需要所以关闭了): 注意,这个图仅作示意,为了帮助大家先初步搞懂父进程和子进程还有管道文件之间的关系,实际后续实现时我们可能会有别的设计,或许实现后的真实关系并不像下图这样,请大家注意不要混淆.


进程池实现整体思路

进程池整体思路如下图:


进程池实现完整代码

Test.hpp文件

代码语言:javascript
代码运行次数: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<< "用户释放技能,更新技能cd及蓝量"<<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);
}

ProcessPool.cc文件

代码语言:javascript
代码运行次数:0
运行
复制
#include"Task.hpp"

#include<string>
#include<vector>
#include<cstdlib>
#include<cassert>
#include<unistd.h>
#include<time.h>
#include<sys/wait.h>
#include<sys/stat.h>

const int processnum = 10;
std::vector<task_t> tasks;

//先描述
class channel
{
public:
    channel(int cmdfd, pid_t slaverid, std::string &processname)
    :_cmdfd(cmdfd)
    ,_slaverid(slaverid)
    ,_processname(processname)
    {}

public:
    int _cmdfd;                  //发送任务的文件描述符
    pid_t _slaverid;             //子进程的PID
    std::string _processname;    //子进程的名字 -- 方便我们打印日志
};


void slaver()
{
    while(true)
    {
        int cmdcode = 0;
        int n = read(0, &cmdcode, sizeof(int));;
        if(n == sizeof(int))
        {
            //执行cmdcode对应任务列表
            std::cout <<"slaver say@ get a command  " << getpid() << "  cmdcode:" << cmdcode << std::endl;
            if(cmdcode >= 0 && cmdcode < tasks.size()) tasks[cmdcode]();
        }
        if(n == 0) break;
    }
}

//编码规范
//输入       const &
//输出       *
//输入输出   &
void InitProcessPool(std::vector<channel> *channels)
{
    //确保子进程只有一个写端
    //使用数组是因为父进程的写端一直存在,开了多少子进程就会有多少写端
    //子进程越靠后,从父进程继承的越多
    std::vector<int> oldfds;


    for(int i=0; i<processnum; i++)
    {
        int pipefd[2];         //临时空间
        int n = pipe(pipefd);  //创建管道
        assert(!n);
        (void)n;

        pid_t id = fork();
        if(id == 0)   //child
        {
            for(auto fd : oldfds) close(fd);//关闭子进程继承的父进程的写端
            close(pipefd[1]);
           
            dup2(pipefd[0],0);  //替换子进程的标准输入端和管道输入端,这样后续子进程需要读数据直接从0号文件读就行
            close(pipefd[0]);

            slaver();
            std::cout<<"process : "<<getpid()<<" quit"<<std::endl;

            exit(0);
        }
        //father
        close(pipefd[0]);

        //添加channel字段
        std::string name = "process-" + std::to_string(i);
        channels->push_back(channel(pipefd[1], id, name));
        oldfds.push_back(pipefd[1]);
    }
}

void Debug(const std::vector<channel> &channels)
{
    //test
    for(const auto &c : channels)
    {
        std::cout<<c._cmdfd<<" "<<c._slaverid<<" "<<c._processname<<std::endl;
    }
}

void Menu()
{
    std::cout<<"******************************"<<std::endl;
    std::cout<<"*         1.刷新日志          *"<<std::endl;
    std::cout<<"*         2.刷新野怪          *"<<std::endl;
    std::cout<<"*         3.检测软件更新      *"<<std::endl;
    std::cout<<"*         4.更新cd及蓝量      *"<<std::endl;
    std::cout<<"*         0.退出             *"<<std::endl;
    std::cout<<"******************************"<<std::endl;
}

void ctrlSlaver(const std::vector<channel> &channels)
{
    int which = 0;
    
    //for(int i = 1;i<=10;i++)
    //int cnt = 5;
    while(true)
    {
        //用户手动控制
        int select = 0;
        Menu();
        std::cout<<"please Enter@ ";
        std::cin>>select;

        if(select <= 0 || select >= 5) break;
        //1.选择任务
        //int cmdcode = rand()%tasks.size();
        int cmdcode = select - 1;
        //2.选择进程  :要负载均衡1.随机数2.轮询
        //随机选择
        //int processpos = rand()%channels.size();

        std::cout<<"father say: "<<"cmdcode: "<<cmdcode
                <<" already sendto "<<channels[which]._slaverid
                <<" process name: "<<channels[which]._processname
                <<std::endl;

        //3.发送任务  :向管道里发送数据(任务编码)
        write(channels[which]._cmdfd,&cmdcode,sizeof(cmdcode));

        //轮询选择
        which++;
        which%=channels.size();

        //cnt--;
        sleep(1);
    }
}

void QuitProcess(const std::vector<channel> &channels)
{
    for(const auto &c : channels) 
    {
        close(c._cmdfd);
        waitpid(c._slaverid,nullptr,0);
    }

    //for(const auto &c : channels) close(c._cmdfd);
    //sleep(5);
    //for(const auto &c : channels) waitpid(c._slaverid,nullptr,0);
    //sleep(5);

    //因为有子进程会继承父进程对其他进程的管道的写端的问题,
    //所以需要倒着回收进程,这样从后向前依次消解多人指向管道文件导致文件无法关闭的情况
    // int last = channels.size()-1;
    // for(int i=last; i>=0;i--)
    // {
    //     close(channels[i]._cmdfd);
    //     waitpid(channels[i]._slaverid,nullptr,0);
    // }
}

int main()
{
    
    LoadTask(&tasks);

    srand(time(nullptr)^getpid()^1023);  //种随机数种子
    //再组织
    std::vector<channel> channels;
    

    //1.初始化
    InitProcessPool(&channels);
    Debug(channels);

    //2.开始控制子进程
    ctrlSlaver(channels);

    //3.清理收尾 :关闭创建的所有进程
    QuitProcess(channels);

    return 0;
}

makefile文件

代码语言:javascript
代码运行次数:0
运行
复制
ProcessPool:ProcessPool.cc
	g++ -o $@ $^ -g -std=c++11

.PHONY:clean
clean:
	rm -f ProcessPool

结语

希望这篇关于 实现一个简易的进程池 的博客能对大家有所帮助,欢迎大佬们留言或私信与我交流.

学海漫浩浩,我亦苦作舟!关注我,大家一起学习,一起进步!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 池化技术
  • 进程池的设计原理
  • 进程池实现整体思路
  • 进程池实现完整代码
    • Test.hpp文件
    • ProcessPool.cc文件
    • makefile文件
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档