Linux--进程池

这次,我们来写一个线程池的非常简陋代码,帮助我们理解

我们之前也讲到过:关于C++的池化技术。

进程池是预先创建一组子进程,统一管理任务分配与资源回收的并发模型

什么是进程池?

进程池是预先创建一组空闲进程,统一管理任务队列,避免频繁创建/销毁线程开销的并发编程模型
思路是:

利用父进程管理所有的子进程,而让子进程去完成某种任务!

我们通过匿名管道来实现:

父进程传输信息给子进程,子进程根据父进程传输的信息来执行不同的任务!

简单来说就是:父进程发出指令,由子进程去执行

为什么进程池能够提高效率?

因为OS不相信用户,因此,我们的执行任务的代码中,必定会由大量的系统调用来构成,而上几篇我们讲到,它调用是有成本的!

现在我们采用:预先创建进程,要用时直接拿,而不是需要用到是再去创建进程。同时,在执行完一个任务的子进程还可以继续去执行另外的任务,从而用重复利用的方法提高了资源效率。

我们知道:管理好事物的本质就是:"先描述,在组织!"

创建对象结构体:

复制代码
class channel
{
public:
    channel(int comfd, pid_t slaverid, std::string processname)
        : _comdfd(comfd), _slaverid(slaverid), _processname(processname)
    {
    }
    // private:
public:
    int _comdfd;              // 发送任务的文件描述符
    pid_t _slaverid;          // 子进程的pid
    std::string _processname; // 子进程的名字,方便打日志
};

我们操作流程主要有:

初始化

控制进程

关闭进程

复制代码
int main()
{
    //模拟执行任务
    LoadTask(&tasks);
    //种下随机数种子
    srand(time(nullptr));
    // 再组织
    std::vector<channel> channels;
    // 初始化,创建多个进程,建立框架!
    InitSliver(&channels);
    // Debug(channels);
    // std::cout<<channels[0]._comdfd<<":"<<channels[0]._slaverid<<":"<<channels[0]._processname<<std::endl;
    //控制进程
    ctrlSlaver(channels);
    QuitSlaver(channels);
    return 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<<"查看用户是否需要登录"<<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);

进程执行任务

复制代码
void sliver()
{
    while(true)
    {
        int cmdcode=0;
        //读取到cmdcode中
        int n=read(0,&cmdcode,sizeof(int));
        if(n==sizeof(int))
        {
            std::cout<<"slaver get a command: "<<getpid()<<" comdcode:"<<cmdcode<<std::endl;
            if(cmdcode>=0 &&cmdcode<tasks.size())
            {
                tasks[cmdcode]();
            }
        }
        if(n==0)
            break;
    }
}

初始化部分

复制代码
//输出型参数 *
//输入型参数const &
//输入输出型参数 &
void InitSliver(std::vector<channel>* channels)
{
    //确保每一个子进程都只有一个写端
    std::vector<int>oldfd;
    for (int i = 0; i < 10; i++)
    {
        int pipefd[2];
        // 建立管道
        int n = pipe(pipefd);
        if (n < 0)
        {
            perror("pipe");
            return;
        }
        // 创建父子进程
        pid_t id = fork();
        if (id < 0)
        {
            perror("fork");
            return;
        }
        else if (id == 0) // child
        {
            for(auto&fd:oldfd)
                close(fd);
            close(pipefd[1]); // 关闭一端
            dup2(pipefd[0], 0);
         
            close(pipefd[0]);
            sliver();
            std::cout<<"process: "<<getpid()<<" quit"<<std::endl;
            exit(0);
            
        }
        // father
        
            close(pipefd[0]); // 关闭一端
            std::string processname = "process" + std::to_string(i);
            channels->push_back({pipefd[1], id, processname});
            oldfd.push_back(pipefd[1]);
            
            // sleep(1);
    }
    
}

简洁菜单:

复制代码
void Menu()
{
    std::cout << "####################################################################" << std::endl;
    std::cout << "####################################################################" << std::endl;
    std::cout << "#######1.检查单词是否有问题    2.检查是否要更新版本###################" << std::endl;
    std::cout << "#######3. 查看下载进度   4.查看用户是否需要登录#######################" << std::endl;
    std::cout << "##########0.退出####################################################" << std::endl;
    std::cout << "####################################################################" << std::endl;
} 

控制进程模块:

复制代码
void ctrlSlaver(const std::vector<channel>& channels)
{
    int count=0;
    while(true)
    {
        int select=0;
        Menu();
        std::cout<<"Please Enter# "<<std::endl;
        std::cin>>select;

        if(select<=0 ||select>5)
            break;
        
        //选择任务
        // int cmdcode=rand()%tasks.size();
        int cmdcode=select-1;
        //选择进程
        int processpos=rand()%tasks.size();
        std::cout<<"father say:"<<"cmdcode:"<<cmdcode<<" already send to "<<channels[processpos]._slaverid<<
            " process name: "<<channels[processpos]._processname<<std::endl;
        //发送任务
        write(channels[processpos]._comdfd,&cmdcode,sizeof(cmdcode));
        count++;
        if(count>=5)
            break;
        sleep(1);
    }

}

关闭进程

复制代码
void QuitSlaver(const std::vector<channel>& channels)
{
    for(const auto e:channels)
    {
        close(e._comdfd);
        waitpid(e._slaverid,nullptr,0);
    }
}

注意:关闭进程这里不能写成这样:

复制代码
  for(const auto &c : channels) close(c._cmdfd);
 
  for(const auto &c : channels) waitpid(c._slaverid, nullptr, 0);
相关推荐
QT 小鲜肉2 小时前
【QT/C++】Qt网络编程进阶:UDP通信和HTTP请求的基本原理和实际应用(超详细)
c语言·网络·c++·笔记·qt·http·udp
人工智能训练2 小时前
在ubuntu系统中如何将docker安装在指定目录
linux·运维·服务器·人工智能·ubuntu·docker·ai编程
轻舟客丶2 小时前
ORA-03113的解决方案
数据库·经验分享·笔记·oracle
Lisonseekpan2 小时前
Linux 常用命令详解与使用规则
linux·服务器·后端
老虎06272 小时前
黑马点评学习笔记07(缓存工具封装)
笔记·学习·缓存
青木川崎2 小时前
linux面试题
java·linux·运维
大邳草民3 小时前
深入理解 Python 的“左闭右开”设计哲学
开发语言·笔记·python
实心儿儿3 小时前
C++ —— list
开发语言·c++
仟千意3 小时前
C++:c++基础知识
c++