【Linux】进程池

news2024/11/29 4:34:27

目录

进程池

进程池的概念:

手搓进程池:

1、创建信道和子进程

2、通过channel控制子进程

3、回收管道和子进程


进程池

进程池的概念:

        定义一个池子,在里面放上固定数量的进程,有需求来了,就拿一个池中的进程来处理任务,等到处理完毕,进程并不关闭,直接将进程再放回进程池中继续等待任务;
        如果有很多任务需要执行,池中的进程数量不够,任务就要等待之前的进程执行任务完毕归来,拿到空闲进程才能继续执行。也就是说,进池中进程的数量是固定的,那么同一时间最多有固定数量的进程在运行;这样不会增加操作系统的调度难度,还节省了开关进程的时间,也一定程度上能够实现并发效果。

看下图,父进程和子进程之间可以通过管道来交互;

如果管道中没有数据,则worker进程就阻塞等待;master向哪个管道写入就唤醒哪一个子进程来处理任务;

手搓进程池:

1、创建信道和子进程

我们用一个类来录父进程读写端的fd和子进程的id,用vector来存储;

  • 先来创建一个管道(pipe)
  • 管道创建成功后,再创建子进程(fork)
  • 关闭不需要的fd
  • 将信息存储到vector中

可以将上述代码封装到一个函数中,这样比较好看

#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
using namespace std;

class channel
{
public:
    channel(int wfd, pid_t id, const string &name) : _wfd(wfd), childid(id), _name(name)
    {
    }
    ~channel()
    {
    }
    int getwfd() { return _wfd; }
    pid_t getid() { return childid; }
    string getname() { return _name; }

private:
    int _wfd;
    pid_t childid;
    string _name;
};

void work(int rfd)
{
}

void create(vector<channel> &channels, int num)
{
    for (int i = 0; i < num; i++)
    {
        int pipfd[2] = {0};
        int n = pipe(pipfd);
        pid_t id = fork();
        if (id == 0)
        {
            // child --read
            close(pipfd[1]);
            work(pipfd[0]);
            close(pipfd[0]);
            exit(0);
        }
        // father --write
        close(pipfd[0]);
        string name = "channel-" + to_string(i);
        channels.push_back(channel(pipfd[1], id, name));
        close(pipfd[1]);
    }

}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        cerr << "processnum???" << endl;
        return 1;
    }
    int num = stoi(argv[1]);
    // 1、创建子进程和信道
    vector<channel> channels;
    create(channels, num);

    for (auto channel : channels)
    {
        cout << channel.getid() << " " << channel.getwfd() << " " << channel.getname() << endl;
    }
    
}

运行结果:

2、通过channel控制子进程

信道建立好后,下面就是接收主进程给我们的任务就可以了,可是子进程如何接收和识别任务呢?

这里我们可以开一个hpp文件来模拟我们的任务:

.hpp文件是允许声明和实现写到一起的;

 在这个文件中使用函数指针类型来初始化,并且有随机选择任务的函数,执行任务的函数;

#pragma once

#include <iostream>
#include <ctime>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std;

#define NUM 3
typedef void (*task_t)();

task_t tasks[NUM];

// 创建任务
void Print()
{
    cout << "I am Print Task" << endl;
}
void Flush()
{
    cout << "I am Flush Task" << endl;
}
void Download()
{
    cout << "I am Download Task" << endl;
}

// 初始化
void Loadtask()
{
    srand(time(nullptr) ^ getpid());
    tasks[0] = Print;
    tasks[1] = Download;
    tasks[2] = Flush;
}

void Excutetask(int num)
{
    if (num < 0 || num > 2)
        return;
    tasks[num]();
}

int selecttask()
{
    return rand()%NUM;
}

完成.hpp文件后,在我们的.cpp文件中添加对应的头文件;

  • 随机选择一个任务
  • 选择信道和进程
  • 发送任务----父进程完成write操作,子进程完成read操作

 

运行结果:

3、回收管道和子进程
  • 关闭所有w端
  • wait,回收子进程

完整代码:

#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include "test.hpp"
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;

class channel
{
public:
    channel(int wfd, pid_t id,const string &name) : _wfd(wfd), childid(id), _name(name)
    {
    }
    ~channel()
    {
    }
    int getwfd() { return _wfd; }
    pid_t getid() { return childid; }
    string getname() { return _name; }

    void closechannel()
    {
        close(_wfd);
    }
    void wait()
    {
        pid_t rid =waitpid(childid,nullptr,0);
        if(rid>0)
        {
            cout<<"wair sucess"<<endl;
        }
    }

private:
    int _wfd;
    pid_t childid;
    string _name;
};

void work(int rfd)
{
    while (true)
    {
        int command = 0;
        int n = read(rfd, &command, sizeof(command));
        if (n == sizeof(int))
        {
            Excutetask(command);
        }
    }
}

void create(vector<channel> &channels, int num)
{
    for (int i = 0; i < num; i++)
    {
        int pipfd[2] = {0};
        int n = pipe(pipfd);
        pid_t id = fork();
        if (id == 0)
        {
            // child --read
            close(pipfd[1]);
            work(pipfd[0]);
            close(pipfd[0]);
            exit(0);
        }
        // father --write
        close(pipfd[0]);
        string name = "channel-";
        name += to_string(i);
        channels.push_back(channel(pipfd[1], id, name));
    }
}

int selectchannel(int num)
{
    static int index = 0;
    int next = index;
    index++;
    index %=num;
    return next;
}
void send(int selectnum, int channel_index, vector<channel> &channels)
{
    write(channels[channel_index].getwfd(), &selectnum, sizeof(selectnum));
}

void controlonce(vector<channel> &channels)
{
    // 2.1、选一个任务
    int selectnum = selecttask();
    // 2.2、选一个信道和进程
    int channel_index = selectchannel(channels.size());
    // 2.3、发送---父进程w,子进程r
    send(selectnum, channel_index, channels);
    cout << "信息发送成功" << endl;
}
void control(vector<channel> &channels, int times = -1)
{
    if (times > 0)
    {
       while(times--)
       {
         controlonce(channels);
       }
    }
    else
    {
        while (true)
        {
            controlonce(channels);
        }
    }
}

void clean(vector<channel> &channels)
{
    for(auto channel:channels)
    {
        channel.closechannel();
    }
    for(auto channel:channels)
    {
        channel.wait();
    }
}

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        cerr << "processnum???" << endl;
        return 1;
    }
    int num = stoi(argv[1]);
    Loadtask();
    // 1、创建子进程和信道
    vector<channel> channels;
    create(channels, num);
    // for (auto channel : channels)
    // {
    //     cout << channel.getid() << " " << channel.getwfd() << " " << channel.getname() << endl;
    // }
    // 2、通过channel控制子进程
    control(channels, 10);

   //3、回收管道和子进程
   clean(channels);
}

以上就是进程池的知识点,希望有所帮助!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2218734.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux fork函数

目录 0.前言 1.fork函数初识 2.写时拷贝 3.fork常规用法 4.fork调用失败的原因 5.小结 &#xff08;图像《分叉之光&#xff1a;科幻视角下的Linux进程复制》由AI生成&#xff09; 0.前言 在Linux操作系统中&#xff0c;进程是系统资源管理的核心单元。每一个程序的执行都对…

机器学习(MachineLearning)(8)——模型评估与优化

机器学习&#xff08;MachineLearning&#xff09;&#xff08;1&#xff09;——机器学习概述 机器学习&#xff08;MachineLearning&#xff09;&#xff08;2&#xff09;——线性回归 机器学习&#xff08;MachineLearning&#xff09;&#xff08;3&#xff09;——决策树…

Java网络编程-简单的API调用

Get请求 - 无参数 安装依赖库 首先需要安装一个库&#xff1a; Okhttp3&#xff0c;这是一个非常流行的 HTTP 库&#xff0c;可以简单、快速的实现 HTTP 调用。 安装 Okhttp3 的方式是在 pom.xml 文件中增加依赖&#xff1a; <!-- https://mvnrepository.com/artifact/co…

【Vue.js设计与实现】第三篇第9章:渲染器-简单Diff算法-阅读笔记

文章目录 9.1 减少 DOM 操作的性能开销9.2 DOM 复用与 key 的作用9.3 找到需要移动的元素9.4 如何移动元素9.5 添加新元素9.6 移除不存在的元素 系列目录&#xff1a;【Vue.js设计与实现】阅读笔记目录 当新旧vnode 的子节点都是一组节点时&#xff0c;为了以最小的性能…

PSPICE FOR TI笔记记录1

快捷放置器件 R旋转 连线 w,单击器件引脚方块部分 电压探测笔 创建仿真文件 Analysis Type 分析模式&#xff1a;比如时域分析&#xff0c;频域分析 Run To Time 仿真时长 Skip intial transient bias point calculation (跳过初始瞬态偏置点计算(SKIPBP))一定要勾选 编辑…

高级语言源程序转换为可执行目标文件

将高级语言源程序转换为可执行目标文件的过程通常包括以下几个主要步骤&#xff1a; ​ 1. 预处理&#xff08;Preprocessing&#xff09;&#xff1a; 由谁完成预处理器&#xff08;cpp&#xff09;操作处理源代码中的预处理指令&#xff08;如宏定义、文件包含、条件编译等&…

k8s 1.28.2 集群部署 harbor v2.11.1 接入 MinIO 对象存储

文章目录 [toc]提前准备什么是 HarborHarbor 架构描述Harbor 安装的先决条件硬件资源软件依赖端口依赖 Harbor 在 k8s 的高可用Harbor 部署Helm 编排YAML 编排创建 namespace导入镜像部署 Redis部署 PostgreSQL部署 Harbor core部署 Harbor trivy部署 Harbor jobservice部署 Ha…

《Sui区块链:重塑去中心化应用的新星与未来潜力》

目录 引言 一、Sui 1、 技术架构 2、 编程语言 3、Move起源 4、Move的几个关键点&#xff1a; 5、Move 智能合约编程语言 6、智能合约编程语言可以做什么 7、和其他编程语言有什么不同 8、 安全性 9、开发者体验 10、生态系统 11、 未来发展 总结 引言 在区块链技…

AIGC助力小学生编程梦:C++入门不再难!

文章目录 一、AIGC时代下的编程教育新趋势二、小学生C入门趣味编程的意义三、小学生C入门趣味编程的实践策略四、面临的挑战与应对策略五、AIGC技术在小学生C编程中的应用与前景《小学生C趣味编程从入门到精通》编辑推荐内容简介作者简介目录 随着人工智能生成内容&#xff08;…

基于百度智能体开发爱情三十六计

基于百度智能体开发爱情三十六计 文章目录 基于百度智能体开发爱情三十六计1. 爱情三十六计智能体2. 三十六计开发创意3. 智能体开发实践3.1 基础配置3.2 进阶配置3.3 调优心得3.4可能会遇到的问题 4. 为什么选择文心智能体平台 1. 爱情三十六计智能体 爱情三十六计 是一款基于…

DORA 机器人中间件学习教程(6)——激光点云预处理

文章目录 1 移植思路2 代码输入输出说明3 编写CmakeList.txt文件4 编写yml文件5 编译并启动节点参考资料 在DORA中通过驱动获取激光雷达数据后&#xff0c;激光点云预处理部分代码是参考了autoware官方代码并对其进行裁剪得到的&#xff0c;点云预处理主要包含三个节点&#xf…

vue3项目使用百度地图实现地图选择功能代码封装(开箱即用)

vue3项目使用百度地图实现地图选择功能代码封装方案(开箱即用) <template><div class="bmapgl">

音视频入门基础:FLV专题(15)——Video Tag简介

一、引言 根据《video_file_format_spec_v10_1.pdf》第75页&#xff0c;如果某个Tag的Tag header中的TagType值为9&#xff0c;表示该Tag为Video Tag&#xff1a; 这时StreamID之后紧接着的就是VideoTagHeader&#xff0c;也就是说这时Tag header之后的就是VideoTagHeader&…

MySQL常用命令大全

博客主页&#xff1a;长风清留扬-CSDN博客系列专栏&#xff1a;MySQL入门到入魔每天更新大数据相关方面的技术&#xff0c;分享自己的实战工作经验和学习总结&#xff0c;尽量帮助大家解决更多问题和学习更多新知识&#xff0c;欢迎评论区分享自己的看法感谢大家点赞&#x1f4…

windows mysql 8.0版本重置root密码

1.停止mysql服务 以管理员运行cmd 2.安全模式启动 mysqld --console --skip-grant-tables --shared-memory 3.修改密码 再开个cmd窗口就可以进入了&#xff1a;mysql 先进入mysql database&#xff1a;use mysql 修改密码&#xff1a;ALTER USER rootlocalhost IDENTIFIED …

FFmpeg 4.3 音视频-多路H265监控录放C++开发二 : 18.04ubuntu安装,linux 下build ffmpeg 4.3 源码 并测试

测试环境 ubuntu 18.04 64 位&#xff0c;安装vmware and ubuntu 安装后调整 分辨率&#xff1a; 让windows 可以和 linux 互相复制黏贴 sudo apt-get autoremove open-vm-tools sudo apt-get update sudo apt-get install open-vm-tools-desktop 一直Y reboot 依赖安装 sud…

快速在找到函数的实体的方法

当我们写了许多许多的函数&#xff0c;那我们怎么快速的找到他们呢 我们只需要按下ctrl&#xff0c;在点击函数名字就可以快速的找到我们想要的函数

从Apple Intelligence到远程机器人手术:更快、更安全的网络成企业业务关键

过去&#xff0c;企业的业务模式和网络架构相对简单&#xff0c;数据传输量不大&#xff0c;远程访问需求也不多。企业对网络的要求主要集中在确保基本的连通性和可用性。如今&#xff0c;企业通过将产品与各项高新技术深度融合&#xff0c;赋予传统产品活力和竞争力。以苹果公…

C++20中头文件span的使用

<span>是C20中新增加的头文件&#xff0c;此头文件是containers库的一部分。包括&#xff1a; 1.模板类std::span&#xff1a;连续对象序列的非拥有视图(view)。std::span可以具有static extent&#xff0c;在这种情况下&#xff0c;序列中的元素数量在编译时已知并以typ…

06.队列介绍+实现

目录 一、队列的概念 二、队列的实现 1、头文件定义 2、功能函数实现 3、主函数测试 一、队列的概念 队列就像吃饭排队类似&#xff0c;先来先吃&#xff0c;先进先出。 队头&#xff1a;队列的头部。 队尾&#xff1a;队列的尾部。 入队&#xff1a;在队尾操作。 出队&…