Linux多进程(五) 进程池 C++实现

news2025/1/18 16:50:22

一、进程池的概念

1.1、什么是进程池

进程池是一种并发编程模式,用于管理和重用多个处理任务的进程。它通常用于需要频繁创建和销毁进程的情况,以避免因此产生的开销。

进程池的优点包括:

  1. 减少进程创建销毁的开销:避免频繁创建和销毁进程所带来的系统资源开销。
  2. 提高系统响应速度:由于进程已经初始化并且一直保持在内存中,可以立即分配执行任务,减少了任务等待时间。
  3. 控制资源使用:通过限制进程池中的进程数量,可以控制系统资源的使用情况,避免资源过度消耗。

1.2、管理进程

预先创建一些空闲进程,管理进程会把工作分发到空闲进程来处理,空闲进程处理结束后,通知管理进程。

管理进程需要将任务发给空闲的工作进程,这里就涉及到进程之间的通信,进程间的通信有以下三种方式,我们之前都做了详细的解释

管道:https://blog.csdn.net/weixin_43903639/article/details/138155634?spm=1001.2014.3001.5501

消息队列:https://blog.csdn.net/weixin_43903639/article/details/138155723?spm=1001.2014.3001.5501

共享内存:https://blog.csdn.net/weixin_43903639/article/details/138189200?spm=1001.2014.3001.5501

二、进程池模型

对于一个进程池,我们需要维护一个进程队列,如果进程在忙就等待,如果进程空闲,那么就给空闲进程发任务让进程去处理。

请添加图片描述

三、进程通信

我们这里采用了有名管道的方式。实际上使用匿名管道是一样的。为了方便管理,我们这里创建了一个Fifo的类,通过类将管道视为一个个的对象。

// 权限
#define Mode 0666
// 文件地址
#define Path "./default"

class Fifo
{
public:
    Fifo(string path = Path, int mode = Mode) : _path(path), _mode(mode)
    {
        int return_mkfifo_val = mkfifo(_path.c_str(), _mode);
        if (return_mkfifo_val < 0)
        {
            cout << "mkfifo error:" << errno << " reason :" << strerror(errno) << endl;
            exit(1);
        }
        cout << "mkfifo success" << endl;
    }

    ~Fifo()
    {
        int return_unlink_val = unlink(_path.c_str());
        if (return_unlink_val < 0)
        {
            cout << "unlink error:" << errno << " reason :" << strerror(errno) << endl;
        }
        cout << "unlink namepipe success" << endl;
    }

private:
    string _path;
    int _mode;
};

管道的默认权限是 0666,管道的默认文件地址是 ./default,这样管理的优势是便于管道的创建与销毁。

三、进程对象

我们将一个进程也视为一个对象,那么一个进程就需要以下的元素

  • fd0 : 管道,通过这个管道接收主进程的数据
  • fd1:管道,通过这个管道给主进程发数据
  • pid:子进程的pid
  • isbusy:此子进程是否在忙
class Process
{
public:
    Process(int fd0, int fd1, pid_t process_pid) : _fd0(fd0), _fd1(fd1), _process_pid(process_pid), _isbusy(false) {}

    ~Process() {}

    // 获取父进程要写入的管道
    int get_fd0() { return _fd0; }

    // 获取子进程要写入的管道
    int get_fd1() { return _fd1; }

    // 获取子进程pid
    pid_t get_process_pid() { return _process_pid; }

    // 获取是否忙碌标志位
    bool get_isbusy() { return _isbusy; }

    // 修改标志为
    void set_isbusy(bool flag) { _isbusy = flag; }

private:
    int _fd0;           // 父进程要写入的管道
    int _fd1;           // 父进程要读入的管道
    pid_t _process_pid; // 子进程pid
    bool _isbusy;       // 是否忙碌标志位
};

四、进程池

进程池就是同时管理管道和进程的。其中包含多个进程对象,每个进程对象又要包含两个管道。

  • vector<string> pipe0 主进程写入,子进程读的管道名
  • vector<string> pipe1 子进程写入,主进程读的管道名
  • vector<Fifo *> fifo0 主进程写入,子进程读的管道
  • vector<Fifo *> fifo1 子进程写入,主进程读的管道
  • vector<Process> _processpool 进程池管理的进程
  • int _processnum 进程池管理的进程数量
// 进程池
class ProcessPool
{
public:
    ProcessPool(int processnum) : _processnum(processnum) {}

    ~ProcessPool()
    {
        for (int i = 0; i < _processnum; i++) {
            delete fifo0[i];
            delete fifo1[i];
        }
        // 要释放所有的子进程
        for (int i = 0; i<_processnum; i++) {
            // 通知子进程结束,通知失败的话直接杀死子进程
            if (kill(_processpool[i].get_process_pid(), SIGTERM) != 0) {
                // 杀死子进程
                kill(_processpool[i].get_process_pid(), SIGUSR1);
            }
        }
    }

    // 生成管道的名字
    void makePipeName()
    {
        pipe0.clear();
        pipe1.clear();
        for (int i = 0; i < _processnum; i++)
        {
            string s0;
            s0 += "pipe0_" + to_string(i + 1);
            pipe0.push_back(s0);
            string s1;
            s1 += "pipe1_" + to_string(i + 1);
            pipe1.push_back(s1);
        }
    }

    // 创建进程池,接收一个参数的函数指针
    void CreateProcessPool(work_t work = worker)
    {
        for (int i = 0; i < _processnum; i++)
        {
            // 创建命名管道
            fifo0.push_back(new Fifo(pipe0[i]));
            fifo1.push_back(new Fifo(pipe1[i]));

            int id = fork();
            if (id == 0)
            {
                // 子进程
                int fd0 = open(pipe0[i].c_str(), O_RDONLY);
                int fd1 = open(pipe1[i].c_str(), O_WRONLY);
                work(fd0, fd1);
                exit(0);
            }

            // 父进程打开管道,未发送任务
            int fd0 = open(pipe0[i].c_str(), O_WRONLY);
            int fd1 = open(pipe1[i].c_str(), O_RDONLY | O_NONBLOCK);
            // 设置为非阻塞模式
            fcntl(fd1, F_SETFL, O_NONBLOCK);
            _processpool.push_back({fd0, fd1, id});
        }
    }

    // 找到空闲进程返回进程在_processpool中的序号,没找到返回-1
    int getAChannal()
    {
        for (int i = 0; i < _processnum; i++)
        {
            if (_processpool[i].get_isbusy() == false) {
                _processpool[i].set_isbusy(true);
                return i;
            }
        }
        return -1;
    }

    // 发送任务,任务就是数据
    void SendTask(char *data, int datasize)
    {
        // 随机选中管道
        int luck_process = getAChannal();
        while (luck_process == -1) {
            // 看一下哪些进程是空闲的。每个进程结束后都会发送自己的pid。
            for (int i=0; i<_processnum; i++) {
                pid_t pid = 0;
                ssize_t bytes_read = read(_processpool[i].get_fd1(), &pid, sizeof(pid_t));
                if (bytes_read == -1) continue;
                // 哪个进程返回了数据,就认为Ta结束了。
                else {
                    _processpool[i].set_isbusy(false);
                }
            }
            luck_process = getAChannal();
        }

        // 父进程向管道发送任务,发送任务就是向相应的管道写入数据
        write(_processpool[luck_process].get_fd0(), data, datasize);
    }

    static void worker(int fd0, int fd1)
    {
        char buf[BUFSIZ];
        while (1)
        {
            int read_return_value = read(fd0, buf, BUFSIZ);
            // 处理读取到的数据
            if (read_return_value > 0)
            {
                cout << "my data is : " << buf << " my pid is : " << getpid() << endl;
                // 模拟处理读取到的数据
                sleep(1);
            }
            // 向消息队列传递自己的pid,表示已经完成任务
            pid_t pid = getpid();
            write(fd1, &pid, sizeof(pid_t));
        }
    }

private:
    int _processnum;
    vector<string> pipe0; // 父进程写入,子进程读
    vector<string> pipe1; // 子进程写入,父进程读
    vector<Fifo *> fifo0;
    vector<Fifo *> fifo1;
    vector<Process> _processpool;
};

五、仿真

这里我们的工作函数是默认的工作函数,也就是打印传入的数据。

#include "ProcessPool.h"
#include <iostream>

int main(int argc, char* argv[])
{
    ProcessPool* processpool = new ProcessPool(5);
    processpool->makePipeName();
    processpool->CreateProcessPool();

    char buf[3];

    for (int i=0;i<20;i++) {
        sprintf(buf,"%d",i+100);
        processpool->SendTask(buf,3);
    }

    delete processpool;
    
    return 0;
}

看一下仿真结果

image-20240425194002386

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

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

相关文章

​可视化大屏C位图:城市地图,一览城市全貌信息。

城市地图是一种常见的可视化大屏C位图&#xff0c;用于展示城市的地理分布和相关数据。以下是对城市地图的解读&#xff1a; 地理分布 城市地图可以展示不同地理区域的城市分布情况。通过地图上的点、标记或区域着色等方式&#xff0c;可以清晰地看到城市的位置和分布范围。这…

后端工程师——C++工程师如何准备面试?

相比 Java 语言方向,C++ 入门简单,精通难,找工作竞争压力更小,但 C++ 依然是近年来招聘的热门岗位之一。本文将从以下三个方面进行详细讲解,帮助你对 C++ 相关岗位的就业前景、岗位要求、学习路线等有更充分的了解。 C++工程师面试准备 上两篇文章对 C++ 工程师的招聘需求…

虚拟机VMware下ROS Neotic(Ubuntu 20.04)下安装OpenCV

一、ROS安装 ROS的官方安装步骤&#xff1a; 1、noetic / Ubuntu 20.04 &#xff1a; http://wiki.ros.org/noetic/Installation/Ubuntu 2、melodic / Ubuntu 18.04&#xff1a; http://wiki.ros.org/melodic/Installation/Ubuntu 3、kinetic / Ubuntu 16.04&#xff1a; http:…

如何使用IDEA直接连接MySQL数据库

如何使用IDEA直接连接MySQL数据库 新建一个空项目打开DataBase窗口连接数据库第一次连接 需要先下载驱动上一步驱动下载太慢怎么办&#xff1f;下载好驱动后 测试连接 新建一个空项目 打开DataBase窗口 连接数据库 第一次连接 需要先下载驱动 如果这里下载的很慢 看下一步解决…

算法 || 二分查找

目录 二分查找 在排序数组中查找元素的第一个和最后一个位置 搜索插入位置 一个数组经过划分后具有二段性的都可以用二分查找 二分查找 704. 二分查找 - 力扣&#xff08;LeetCode&#xff09; ​ 暴力解法&#xff1a;直接遍历数组&#xff0c;找到 target 便返回下标&am…

星尘智能 AI 机器人 S1——国产机器人的巅峰之作

AI智能机器人真的太炸裂了 国产科技威武-CSDN直播AI智能机器人真的太炸裂了 国产科技威武https://live.csdn.net/v/382519 最近发现了一个国产的机器人&#xff0c;真的让人惊叹不已&#xff01;它就是星尘智能 AI 机器人 S1&#xff01; 这个机器人简直太牛逼了&#xff01;…

13.JAVAEE之HTTP协议

HTTP 最新的版本应该是 HTTP/3.0 目前大规模使用的版本 HTTP/1.1 使用 HTTP 协议的场景 1.浏览器打开网站 (基本上) 2.手机 APP 访问对应的服务器 (大概率) 学习 HTTP 协议, 重点学习 HTTP 的报文格式 前面的 TCP/IP/UDP 和这些不同, HTTP 的报文格式,要分两个部分来看待.请求…

LeetCode - LCR 008.长度最小的子数组

一. 题目链接 LeetCode - 209. 长度最小的子数组 二. 思路分析 由于此问题分析的对象是「⼀段连续的区间」&#xff0c;因此可以考虑「滑动窗口」的思想来解决这道题。 让滑动窗口满足&#xff1a;从 i 位置开始&#xff0c;窗口内所有元素的和小于target &#xff08;那么当…

Jenkins构建触发器-Git hook自动触发构建

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Jenkins是一个开源…

c++图论基础(1)

目录 无向图 无向图度 无向图性质 有向图 有向图度 有向图性质 图的分类&#xff1a; 稀疏图&#xff1a; 稠密图&#xff1a; 零图&#xff1a; 有向完全图&#xff1a; 无向完全图&#xff1a; 度序列&#xff1a; 图是由顶点集合(简称点集)和顶点间的边(简称边…

【热议】研究生可以跟导师以外的老师合作吗?

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

Linux 权限与软件包管理器 yum

一. 研究Linux默认权限 目录 &#xff0c;起始权限&#xff1a;777 普通文件&#xff0c;起始权限666 Linux系统中存在权限掩码 使用umask指令也可以改变掩码 如果将掩码改为0000 我们可以看到权限发生改变&#xff08;重新设置掩码&#xff09; 最终权限起始权限 去掉 权限…

安卓悬浮窗权限检查

目录 悬浮窗权限代码检测悬浮窗功能 悬浮窗权限 请求了这个权限后&#xff0c;app的权限管理中会有「显示悬浮窗」的权限选项。后面会引导用户去开启这个权限。 <uses-permission android:name"android.permission.SYSTEM_ALERT_WINDOW" />代码检测悬浮窗功能…

目标检测算法YOLOv2简介

YOLOv2由Joseph Redmon等人于2016年提出&#xff0c;论文名为&#xff1a;《YOLO9000: Better, Faster, Stronger》&#xff0c;论文见&#xff1a;https://arxiv.org/pdf/1612.08242.pdf &#xff0c;项目网页&#xff1a;https://pjreddie.com/darknet/yolov2/ ,YOLO9000可以…

做外贸SOHO需要考虑什么条件?

一、理清基本的外贸业务 不想给大家泼冷水&#xff0c;但如果你连基本的外贸知识都不懂&#xff0c;不知道业务怎么进行&#xff0c;想成功可能性为0&#xff01; 在开始 soho前&#xff0c;你需要先了解以下几个方面&#xff1a; 1.产品定位(选品) 2.你的供应商资源(选厂) 3.…

【FreeRTOS】RTOS任务的同步与互斥:(四)事件标志组

【FreeRTOS】RTOS任务的同步与互斥&#xff1a;&#xff08;四&#xff09;事件标志组 事件标志组的概念事件标志组相关 API 函数1. 创建事件标志组2. 设置事件标志位3. 清除事件标志位4. 等待事件标志位 事件标志组案例设计cubeMX配置软件程序设计测试结果 同步与互斥是学习Fr…

图像分类导论:从模型设计到端到端

书籍&#xff1a;An Introduction to Image Classification&#xff1a;From Designed Models to End-to-End Learning 作者&#xff1a;Klaus D. Toennies 出版&#xff1a;Springer Singapore 书籍下载-《图像分类导论》图像分类的传统方法包括在特征空间中进行特征提取和…

深度学习运算:CUDA 编程简介

一、说明 如今&#xff0c;当我们谈论深度学习时&#xff0c;通常会将其实现与利用 GPU 来提高性能联系起来。GPU&#xff08;图形处理单元&#xff09;最初设计用于加速图像、2D 和 3D 图形的渲染。然而&#xff0c;由于它们能够执行许多并行操作&#xff0c;因此它们的实用性…

2024多用户商城系统哪家产品好

在当今激烈的电商竞争中&#xff0c;搭建一个功能强大、性能稳定的多用户商城系统至关重要。针对这一需求&#xff0c;以下是我为您推荐的五款优秀多用户商城系统&#xff0c;它们在功能、定制性、安全性和用户体验方面均表现出色&#xff0c;为您的电商平台搭建提供了可靠的解…

Ubuntu下载的nginx的位置

位置在/etc/nginx 启动nginx systemctl status nginx上面的命令不合适&#xff0c;就重启nginx sudo service nginx restart 关闭nginx nginx -s stop Ubuntu默认的html地址在该文件夹中的default中&#xff1a; /etc/nginx/sites-available if ($http_host ~* "^(w…