当前位置: 首页 > article >正文

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

🦄个人主页:修修修也

🎏所属专栏:Linux

⚙️操作环境:VS Code (操作系统:Ubuntu 22.04 server 64bit)


目录

池化技术

进程池的设计原理

进程池实现整体思路

进程池实现完整代码

Test.hpp文件

ProcessPool.cc文件

makefile文件

结语


池化技术

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

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


进程池的设计原理

        我们一次性创建10个子进程,然后通过一个vector将它们组织起来管理,再建立10个相应的管道和它们通信,为这10个进程发布任务,其底层关系如下图所示(虚线表示创建过联系但不需要所以关闭了):

        注意,这个图仅作示意,为了帮助大家先初步搞懂父进程和子进程还有管道文件之间的关系,实际后续实现时我们可能会有别的设计,或许实现后的真实关系并不像下图这样,请大家注意不要混淆.


进程池实现整体思路

        进程池整体思路如下图:


进程池实现完整代码

Test.hpp文件

#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文件

#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文件

ProcessPool:ProcessPool.cc
	g++ -o $@ $^ -g -std=c++11

.PHONY:clean
clean:
	rm -f ProcessPool

结语

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

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

相关文章推荐

【Linux】进程通信之管道

【Linux】进程间通信

【Linux】实现一个简易的shell命令行

【Linux】操作系统与进程

【Linux】实现三个迷你小程序(倒计时,旋转指针,进度条)

【Linux】手把手教你从零上手gcc/g++编译器

【Linux】手把手教你从零上手Vim编辑器

【Linux】一文带你彻底搞懂权限

【Linux】基本指令(下)

【Linux】基本指令(中)

【Linux】基本指令(上)


 


http://www.kler.cn/a/399881.html

相关文章:

  • ArcGIS Pro ADCore DAML
  • 2.5D视觉——Aruco码定位检测
  • ubuntu20.04 更换清华源报错
  • Python 神经网络项目常用语法
  • 鸿蒙网络编程系列48-仓颉版UDP回声服务器示例
  • Ubuntu22.04基于ROS2-Humble安装moveit2教程(亲测)
  • 中国省级金融发展水平指数(金融机构存款余额、贷款余额、GDP)2020-2023年
  • 整理:4篇专注于多模态大语言模型(MLLM)的瘦身变体论文
  • 一文说清:c++标准库
  • 电脑msvcr100.dll丢失的解决方法,详细介绍多个解决方法
  • 上市公司企业研发操纵数据集(2008-2023年)
  • 【鸿蒙开发】第十五章 H5与端侧交互、Cookies以及Web调试
  • Video Duplicate Finder 快速识别并去除重复的视频和图像!
  • 大数据算法:三、梯度下降
  • Linux 中怎样把正在执行的任务放到后台执行
  • vue3 路由写法及传参方式 !超详细
  • 开源物业管理系统助力智能社区提升服务效率与用户体验
  • 基于Java Springboot北工国际健身俱乐部系统
  • 数据结构哈希表-(开放地址法+二次探测法解决哈希冲突)(创建+删除+插入)+(C语言代码)
  • 用Rust TypeMap安全存储多种类型数据
  • 【知识科普】统一身份认证CAS
  • PGMP-练DAY26
  • django的model时间怎么转时间戳
  • SWPUCTF 2024 奇迹新生塞 签到?
  • 【微信小程序】访客管理
  • 基于扩散模型的无载体图像隐写术