【进程通信】利用管道创建进程池(结合代码)

文章目录

  • 什么叫进程池
    • 进程池的优点
  • 创建进程池
    • 代码实现:

什么叫进程池

我们知道,一个进程创建子进程通常是为了让这个子进程去为它完成某个任务。例如我们使用的指令,其实就是bash进程创建子进程让子进程去执行的。但是我们需要考虑这样一个问题:是不是遇到问题之后才创建子进程呢?

频繁的创建和销毁进程都是一项较大的开销,涉及到内存分配、上下文切换等操作。于是我们可以提前创建出一批进程,当有任务要做的时候就从这一批子进程中拿出一个空闲的去执行,一旦某个子进程把任务执行完之后也不立即销毁进程,而是等待下一个任务的来临。

我们把这些预先创建的一批子进程就叫做进程池。我们把进程池中的进程也称为工作进程

进程池的优点

  1. 通过复用已经创建的进程,减少创建和销毁进程的开销,提高了系统资源的利用与率和系统的性能。
  2. 动态调整进程池中工作进程的数量。可以让系统根据实际的负载和任务需求,动态的增加或者减少工作进程的数量,以适应不同的工作负载。提高了系统的工作的灵活性
  3. 简化编程。进程池提供了一种高级抽象,隐藏了底层进程管理的细节,使得并发进程更加简单和可靠。

创建进程池

现在我们尝试自己去实现一个进程池。
创建进程好很简单,如何管理这些进程呢?每创建一个进程我们都用一个结构体(或者类)维护,于是对工作进程的管理就变成了对结构体的管理,整个进程池从代码角度上来说就是一个结构体数组。

那我们如何将任务分配到工作进程呢?这个问题的本质其实是在问进程之间通信的方式。于是我们很容易的想到使用管道。父进程通过管道,将任务信息传递给子进程。

下面是使用进程池的一个简单模型:

在这里插入图片描述

代码实现:

Process.cpp文件


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

// master
class Channel // 维护管道信息
{
public:
    Channel(int wfd, int subprocessid, string name)
        : _wfd(wfd), _subprocessid(subprocessid), _name(name)
    {
    }

    int GetWfd() { return _wfd; }
    int GetProcessId() { return _subprocessid; }
    string GetName() { return _name; }

    void CloseChannel()
    { // 关闭信道的写端
        close(_wfd);
    }

    void Wait()
    { // 关闭工作进程
        int status = 0;
        pid_t rid = waitpid(_subprocessid, NULL, 0);
        if (rid > 0)
        {
            cout << "wait " << rid << " success" << endl;
        }
    }

    ~Channel()
    {
    }

private:
    int _wfd;          // 信道以写方式打开的文件描述符
    int _subprocessid; // 读端进程的pid
    string _name;      // 信道的命名
};

// 创建信道和子进程
void CreatChannelAndSub(int num, vector<Channel> &Channels, task_t task)
{ // task是一个回调函数
    for (int i = 0; i < num; i++)
    {
        // 1.创建管道
        int pipefd[2];
        int n = pipe(pipefd);
        if (n < 0)
            exit(0);

        // 创建子进程
        pid_t pid = fork();
        if (pid == 0)
        {
            // 关闭写端
            close(pipefd[1]);
            dup2(pipefd[0], 0); // 将管道的读端重定向到标准输入
            task();             // 子进程执行的任务
            close(pipefd[0]);
            exit(0);
        }
        // 父进程关闭读端,只用来输出任务
        close(pipefd[0]);
        string name = "Channel-" + to_string(i);
        // 将创建的信道存入输出型参数中
        Channels.push_back({pipefd[1], pid, name});
    }
}

int NextChannel(int channelnum)
{
    static int next = 0;
    int channel = next;
    next = (next + 1) % channelnum;
    return channel;
}

void SendTask(int taskcommand, Channel &channel)
{
    write(channel.GetWfd(), &taskcommand, sizeof(taskcommand));
}

// 具体任务
void CtrlProcessOnce(vector<Channel> &Channels)
{
    // 1.选择一个任务
    int taskcommand = SelectTask();
    // 2.选择一个空信道
    int taskchannel = NextChannel(Channels.size());

    // 3.发送任务编号给信道
    SendTask(taskcommand, Channels[taskchannel]);

    cout << "taskcommand: " << taskcommand << " channel: " << Channels[taskchannel].GetName() << " sub process: " << Channels[taskchannel].GetProcessId() << endl;
}

// 控制子进程执行任务
void CtrlProcess(vector<Channel> &Channels, int time = -1)
{ // time表示分配多少次任务给子进程去执行,默认是无限
    if (time == -1)
    { // 默认
        while (true)
        {
            CtrlProcessOnce(Channels);
            sleep(1);
        }
    }
    else if (time > 0)
    {
        while (time--)
        {
            CtrlProcessOnce(Channels);
            sleep(1);
        }
    }
}

void CleanUpChannel(vector<Channel> &channels)
{
    for (auto &it : channels)
    {
        it.CloseChannel();
    }

    for (auto &it : channels)
    {
        it.Wait();
    }
}

int main(int argc, char *argv[])
{ // 参数列表,argv[1]表示创建的工作进程的数量,即内存池大小
    if (argc != 2)
    {
        cerr << "Usage: " << argv[0] << " Processnum" << endl;
        return 1;
    }
    int num = stoi(argv[1]);
    // 加载任务
    LoadTask();
    vector<Channel> Channels; // 进程池

    // 创建信道和子进程
    CreatChannelAndSub(num, Channels, work1);

    // 通过Channel控制子进程
    CtrlProcess(Channels, 10);
    // 关闭子进程和信道的写端
    CleanUpChannel(Channels);
    return 0;
}

Task.hpp文件


#include <iostream>
#include <ctime>
#include <sys/stat.h>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#define TASKNUM 3
using namespace std;

typedef void (*task_t)(); // task_t函数指针类型

void Print()
{ // 任务1
    cout << "Printf task" << endl;
}

void DownLoad()
{ // 任务2
    cout << "DownLoad task" << endl;
}

void Flush()
{ // 任务3
    cout << "Flush task" << endl;
}

task_t tasks[TASKNUM]; // 用来存放函数指针

void LoadTask()
{
    srand(time(nullptr));
    tasks[0] = Print;
    tasks[1] = DownLoad;
    tasks[2] = Flush;
}

void ExcuteTask(int n)
{
    if (n < 0 || n > 2)
        return;

    tasks[n]();
}

int SelectTask()
{
    int n = rand() % TASKNUM;
    return n;
}

void work1()
{
    while (true)
    {
        int command = 0;
        int n = read(0, &command, sizeof command); // 读取管道中的任务编号(int)
        if (n == sizeof(int))
        {
            ExcuteTask(command);
        }
        else if (n == 0)
        {
            cout << "sub process: " << getpid() << " quit " << endl;
            break;
        }
    }
}

观察运行结果:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

RLDP协议原理与应用

RLDP概述 l RLDP全称是Rapid Link Detection Protocol&#xff08;快速链路检测协议&#xff09;&#xff0c;是锐捷网络自主开发的&#xff0c;用于快速检测以太网链路故障的链路协议。 l 一般的以太网链路检测机制都只是利用物理连接的状态&#xff0c;通过物理层的自动协…

React | classnames

classnames 这个库在我们的项目中有大量的使用到&#xff0c;它不仅很实用&#xff0c;还非常好用&#xff0c;但还有人不知道这个库&#xff0c;我真的是十分心痛。 通过 classnames&#xff0c;我们可以给组件设置多个 className&#xff0c;还可以根据需要动态设置 classNa…

机器学习中的CatBoost算法

我们经常遇到包含分类特征的数据集&#xff0c;为了将这些数据集拟合到Boosting模型中&#xff0c;我们对数据集应用各种编码技术&#xff0c;例如One-Hot编码或标签编码。但是应用One-Hot编码会创建一个稀疏矩阵&#xff0c;这有时可能导致模型的过拟合&#xff0c;我们使用Ca…

Oracle中rman使用记录

最近在项目中&#xff0c;遇到使用RMAN的操作来恢复数据库中某个时间归档日志&#xff0c;RMAN的原理和理解&#xff0c;网友们百度了解一下。我重点将实操部分了。直接上实验环节&#xff0c;让网友更懂。&#xff08;特别提醒&#xff1a;我是1:1用VMware克隆数据库进行RMAN还…

分布式与一致性协议之Paxos算法(三)

Paxos算法 兰伯特关于Multi-Paxos的思考 领导者 我们可以通过引入领导者(Leader)节点来解决第一个问题。也就是说将领导者节点作为唯一提议者&#xff0c;如图所示。这样就不存在多个提议者同时提交提案的情况&#xff0c;也就不存在提案冲突的情况了。这里补充一点:在论文中…

开发规范:API安全

开发规范&#xff1a;API安全 API是现代移动、SaaS和web应用程序的关键组成部分&#xff0c;可以应用在面向客户、合作伙伴和内部应用程序中。API可以暴露应用程序逻辑和敏感数据。不安全的API很容易成为黑客攻击的目标&#xff0c;使他们能够访问安全的服务器或网络。攻击者可…

NXP i.MX8系列平台开发讲解 - 3.9 Linux PCIe协议相关介绍(二)

目录 1. PCIe 传输层协议 2. TLP介绍 2.1 TLP包格式 2.2 TLP包的种类 2.3 TLP 包传输例子 2.4 TLP 路由规则 根据上一章的知识&#xff0c;对于PCIe的发展和基础知识有了大概了解&#xff0c;本章节将会讲解PCIe的一些工作原理&#xff0c;使用的协议&#xff0c;通信交互…

挑战一周完成Vue3项目Day2:路由配置+登录模块+layout组件+路由鉴权

一、路由配置 经过分析&#xff0c;项目一共需要4个一级路由&#xff1a;登录&#xff08;login&#xff09;、主页&#xff08;home&#xff09;、404、任意路由&#xff08;重定向到404&#xff09;。 1、安装路由插件 pnpm install vue-router 2、创建路由组件 在src目…

dremio数据湖sql行列转换及转置

1、行转列 (扁平化) 数据准备 表 aa 1.1 cross join unnest 在Dremio中&#xff0c;UNNEST 函数用于将数组或复杂类型的列&#xff08;如JSON、Map或Array类型&#xff09;中的值“炸裂”&#xff08;分解&#xff09;成多行. with aa as ( select 上海 as city, ARRAY[浦东…

2024程诺申论突击理论刷题班

2024程诺申论突击理论刷题班&#xff0c;为备考者提供了系统而高效的申论学习平台。在这个班里&#xff0c;程诺老师以其深厚的理论功底和丰富的教学经验&#xff0c;引导我们深入理解申论的本质和技巧。刷题环节精心设计&#xff0c;让我们在实战中巩固知识&#xff0c;提升能…

Professional CUDA C Programming

2023/4/28 1.使用nvfrof时&#xff0c;报错 解决方法&#xff1a; 将路径 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4\extras\CUPTI\lib64 下的文件cupti64_2020.2.0.dll复制到路径 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.1\bin下即可。 2…

Innodb底层原理与Mysql日志机制到底怎么个事???

在学完Innodb底层原理与Mysql日志机制&#xff0c;自己进行总结&#xff0c;画了一张脑图&#xff0c;思路清晰许多 希望对大家也能有点帮助

Visual Studio Code基础:打开一个编辑器(文件)时,覆盖了原编辑器

相关阅读 VS codehttps://blog.csdn.net/weixin_45791458/category_12658212.html?spm1001.2014.3001.5482 在使用vscode时&#xff0c;偶尔会出现这样的问题&#xff1a;打开了某个编辑器&#xff08;文件&#xff0c;下面统称文件&#xff09;后&#xff0c;再打开其他文件…

安装JAVA和java IDEA并汉化过程

1.安装java: 打开java的下载链接&#xff1a; Java Downloads | Oracle 然后选择对应的版本下载即可&#xff0c;我这里是windows 所以下载这个 然后正常一步步安装即可。 2.配置java环境&#xff1a; 在桌面右键此电脑然后点击属性——高级系统设置——环境变量——然后…

ACE框架学习3

ACE Acceptor-Connector框架 该框架实现 Acceptor-Connector 模式&#xff0c;该模式解除了“网络化应用中的协作对端服务的连接和初始化”与“连接和初始化之后它们所执行的处理”的耦合。Acceptor-Connector 框架允许成用独立于它们所提供的服务来配置其连接布局的关键属性。…

【万字长文】看完这篇yolov4详解,那算是真会了

前言 目标检测作为计算机视觉领域的一个核心任务&#xff0c;其目的是识别出图像中所有感兴趣的目标&#xff0c;并给出它们的类别和位置。YOLO&#xff08;You Only Look Once&#xff09;系列模型因其检测速度快、性能优异而成为该领域的明星。随着YOLOv4的推出&#xff0c;…

网络安全的防护措施有哪些?

1. 安全策略和合规性 2. 物理和网络安全 3. 数据加密 4. 软件和系统更新 5. 访问控制 6. 威胁监测和响应 7. 员工培训和安全意识 8. 备份和灾难恢复 零基础入门学习路线 视频配套资料&国内外网安书籍、文档 网络安全面试题 网络安全的防护措施多种多样&#xff0c…

JVM的垃圾回收机制(GC机制)

在Java代码运行的过程中&#xff0c;JVM发现 某些资源不需要再使用的时候&#xff0c;就会自动把资源所占的内存给回收掉&#xff0c;就不需要程序员自行操作了。“自动回收资源”就是JVM的“垃圾回收机制”&#xff0c;“垃圾回收机制”也称"GC机制"。 对于Java代码…

排序算法(2)快排

交换排序 思想&#xff1a;所谓交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0c;交换排序的特点是&#xff1a;将键值较大的记录向序列的尾部移动&#xff0c;键值较小的记录向序列的前部移动。 一、冒泡排序 public static…

Sarcasm detection论文解析 | 通过阅读进行讽刺推理-Reasoning with sarcasm by reading in-between

论文地址 论文地址&#xff1a;[1805.02856] Reasoning with Sarcasm by Reading In-between (arxiv.org) 论文首页 笔记大纲 通过阅读进行讽刺推理论文笔记 &#x1f4c5;出版年份:2018&#x1f4d6;出版期刊:&#x1f4c8;影响因子:&#x1f9d1;文章作者:Tay Yi,Luu Anh…