进程池详解

news2024/9/20 14:47:38

目录

进程池

1、什么是进程池?

2、实现进程池

(1)相关函数:

pipe函数:

write函数

read函数

waitpid函数

(2)代码实现面向过程进程池

Task.hpp

processPool.cc

3、注意事项


进程池

1、什么是进程池?

提前建立一堆进程,而不是从头开始建立进程
例如,我们执行命令的时候,命令本身也是进程,所以需要bash创建进程
要从头开始创建进程PCB、分配空间等
成本很高
如果每一次都要从头开始建立,很浪费时间成本
所以,为了提高效率,提前建立多个子进程等待
在父进程和子进程之间,建立单向管道通信
当父进程有任务需要子进程执行时,直接把任务写入管道中
对应管道的子进程读取数据,开始执行分配的任务
如果父进程没有分配任务,子进程就处于等待阻塞的状态
当父进程分配任务时,叫做唤醒子进程
所以,这种机制下建立的一系列进程,也需要管理

而管道的作用,就是让进程之间进行协同合作

所以,管理多个进程的容器就叫做进程池(Process Pool)。
 

负载均衡:每个进程执行任务的频度均衡。

2、实现进程池

形参命名规范:

const& :只要输出

&:输出输入型参数

*:输出型参数

(1)相关函数:

pipe函数:
#include <unistd.h>

int pipe(int pipefd[2]);

参数

  • pipefd: 一个整数数组,长度为 2。pipefd[0] 用于读取数据,pipefd[1] 用于写入数据。

返回值

  • 成功:返回 0。

  • 失败:返回 -1,并设置 errno 以指示错误类型。

功能

  • 创建一个管道,管道在内存中提供一个字节流,允许数据从一个进程传递到另一个进程。

  • 管道是一种半双工的通信机制,即数据只能单向流动:要么从写端到读端,要么反之。

write函数
#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

参数

  • fd: 文件描述符,指向要写入的数据流。通常是通过 open 函数、管道、套接字等获得的。

  • buf: 指向要写入的数据缓冲区的指针。

  • count: 要写入的数据字节数。

返回值

  • 成功:返回实际写入的字节数(可能小于 count)。如果写入的数据字节数等于 count,这表示所有请求的数据都已成功写入。

  • 失败:返回 -1,并设置 errno 以指示错误类型。

read函数
#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

参数

  • fd: 文件描述符,指向要读取的数据流。通常通过 open 函数、管道、套接字等获得。

  • buf: 指向接收数据的缓冲区的指针。

  • count: 要读取的最大字节数。

返回值

  • 成功:返回实际读取的字节数。如果返回值为 0,表示已到达文件末尾(EOF)。

  • 失败:返回 -1,并设置 errno 以指示错误类型。

waitpid函数
#include <sys/wait.h>

pid_t waitpid(pid_t pid, int *status, int options);

参数

  • pid: 要等待的子进程的进程 ID。可以是:

    • 特定子进程的进程 ID。

    • -1,表示等待任何子进程(类似于 wait 函数)。

    • 0,表示等待与调用进程在同一进程组的子进程。

    • 小于 -1 的值,表示等待具有相同进程组 ID 的子进程。

  • status: 指向整数的指针,用于存储子进程的退出状态。如果不需要获取状态信息,可以传递 NULL

  • options: 控制函数行为的标志。常见值包括:

    • 0:默认行为。

    • WNOHANG:如果没有子进程状态改变,返回 0,而不是阻塞。

    • WUNTRACED:返回状态改变的子进程,即使它尚未终止。

    • WCONTINUED:返回状态改变的子进程,即使它被继续执行。

返回值

  • 成功:返回子进程的进程 ID。

  • 失败:返回 -1,并设置 errno 以指示错误类型。

(2)代码实现面向过程进程池

Task.hpp
#pragma once
#include <iostream>
#include <string>
#include <stdlib.h>

typedef void (*task_t)(); // task_t类型函数指针数组
#define TaskNum 3

void task1()
{
    std::cout << "i am task1, you can do something in here...." << std::endl;
}
void task2()
{
    std::cout << "i am task2" << std::endl;
}

void task3()
{
    std::cout << "i am task3" << std::endl;
}

task_t tasks[TaskNum];

void LoadTask()
{
    srand(time(nullptr));
    tasks[0] = task1;
    tasks[1] = task2;
    tasks[2] = task3;
}

void ExcuteTask(int tasknum)
{
    if (tasknum < 0 | tasknum > 2)
    {
        return;
    }
    tasks[tasknum](); // 函数指针数组
}

int SelectTask()
{
    return rand() % TaskNum;
}
processPool.cc
#include "task.hpp"
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/wait.h>

// 信道对象
class Channel
{
public:
    // 常见信道,需要进程id,信道名,文件fd
    Channel(int subprocess, std::string &name, pid_t wfd)
        : _name(name), _subprocessid(subprocess), _wfd(wfd)
    {
    }
    
    ~Channel() {}
    int GetWfd() { return _wfd; }
    std::string Getname() { return _name; }
    int Getsubprocessid() { return _subprocessid; }
    void CloseChannel() { close(_wfd); }

    void wait()
    {
        int n = waitpid(_subprocessid, nullptr, 0);
        if (n > 0)
        {
            std::cout << "wait: " << n << " sucess" << std::endl;
        }
    }

private:
    int _wfd;          // 写端
    std::string _name; // 名称
    int _subprocessid; // 进程pid
};

void forTest(std::vector<Channel> *channels)
{
    // for test
    for (auto &channel : *channels) // 解引用
    {
        std::cout << channel.Getname() << " " << channel.Getsubprocessid() << " " << channel.GetWfd() << std::endl;
    }
}

void work(int rfd)
{
    while (true)
    {
        int commd = 0;
        int n = read(rfd, &commd, sizeof(commd));
        if (n == sizeof(int))
        {
            ExcuteTask(commd);
        }
        else if (n == 0)
        {
            std::cout << "subprocess :" << getpid() << " quit sucess" << std::endl;
            break;
        }
    }
}

void creatChannelAndSubprocess(int num, std::vector<Channel> *channels)
{

    // 1.创建管道和进程
    for (int i = 0; i < num; ++i)
    {
        // 创建管道
        int pipefd[2];
        int n = pipe(pipefd); // pipe函数,参数为fd数组,0读,1写
        if (n < 0)
        {
            exit(1);
        }

        // 子进程
        pid_t id = fork(); // 创建子进程,返回值为0
        if (id == 0)
        {
            // child
            close(pipefd[1]); // 关闭写
            work(pipefd[0]);
            close(pipefd[0]);
            exit(0);
        }

        // father
        close(pipefd[0]);
        std::string channel_name = "channel-" + std::to_string(i);
        channels->push_back(Channel(id, channel_name, pipefd[1]));
    }
}

int CurChannel(int channelnum)
{
    static int cur = 0;
    int channel = cur;
    cur++;
    cur %= channelnum;
    return cur;
}

void SentCommd(Channel &channel, int taskcommd)
{
    // 向指定信道和进程发送信号,就是指定管道写东西
    write(channel.GetWfd(), &taskcommd, sizeof(taskcommd));
}

void ctrlSubPro(std::vector<Channel> &channels, int num)
{
    while (num--)
    {
        sleep(1);
        // a.选择任务
        int TaskCommd = SelectTask(); // 任务

        // b.选择信道和进程
        int task_index = CurChannel(channels.size()); // 从所有的信道中选择

        // c.指定信道和进程发送指定任务码
        SentCommd(channels[task_index], TaskCommd);
        std::cout << std::endl;
        std::cout << "TaskCommd: " << TaskCommd << " channels->" << channels[task_index].Getname() << " processid: "
                  << channels[task_index].Getsubprocessid() << std::endl;
    }
}

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

int main(int argc, char *argv[]) // 命令行参数
{
    // 提示,命令行输入进程名 + 进程数
    if (argc != 2)
    {
        std::cout << "Usage: " << argv[0] << " processnum" << std::endl; // usage--用法
        return 1;
    }
    int num = std::stoi(argv[1]);
    std::vector<Channel> channels;
    // 加载任务
    LoadTask();
    // 创建进程+信道
    creatChannelAndSubprocess(num, &channels);

    // 2.控制进程执行任务
    ctrlSubPro(channels, std::stoi(argv[1]));

    // 3.释放资源
    // 关闭写端,回收子进程
    CleanUpChannel(channels);

    return 0;
}

3、注意事项

子进程会继承父进程的文件描述符表,这就会出错
父进程指向1文件,子进程也指向1文件,此时1文件就有两个文件指向

为什么分开关,再回收可以?
实际的关闭过程,先是递归式的从上到下,再从下到上关闭

在创建进程的时候,会因为创建子进程继续父进程的机制,出现多个写端
可以在创建子进程的时候,把历史上所有打开的进程写端口关闭

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

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

相关文章

坚鹏讲人才第12期:引领数字化未来—数字化人才与导师共赢之路

坚鹏讲人才第12期&#xff1a;引领数字化未来—数字化人才与导师共赢之路 ——抢占名额先机 成为坚鹏弟子 加速数字化转型 数字化浪潮汹涌而至&#xff0c;你是否感到迷茫、困惑、焦虑&#xff1f;想不想一脚油门冲进未来&#xff0c;和我一同探寻数字化人才的奥秘&#xf…

【迅为电子】RK3568驱动指南|第十七篇 串口-第202章 串口编程

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

计算机毕业设计选什么题目好?基于vue的音乐播放系统

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

LangGraph Studio:首款智能体(agent)IDE

0 前言 LangGraph Studio 提供了一个专门的智能体IDE&#xff0c;用于可视化、交互和调试复杂的智能体应用程序。本文来了解如何在桌面使用。 LLM为新型智能体应用程序的发展铺平了道路——随这些应用程序演进&#xff0c;开发它们所需工具也必须不断改进。今天推出的 LangG…

C++(10)类语法分析(1)

C(10)之类语法分析(1) Author: Once Day Date: 2024年8月17日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 源码分析_Once-Day的博客-CSDN博客 …

JavaFX布局-DialogPane

JavaFX布局-DialogPane 常用属性标题区域headerTextheader 内容区域contentTextcontent graphic按钮设置expandableContent 实现方式Java实现 一个特殊的布局容器&#xff0c;常用于弹出框&#xff0c;与Dialog配合一起使用包含标题区&#xff0c;内容区域&#xff0c;扩展区域…

Merkle树(Merkle Tree):高效地验证某个数据块是否包含在数据集中

目录 Merkle树(Merkle Tree) 一、基本结构 二、构建过程 三、主要作用 四、应用领域 Merkle树(Merkle Tree) Merkle树(Merkle Tree),也被称为默克尔树或Merkle哈希树,是一种基于哈希的数据结构,主要用于验证大规模数据集的完整性和一致性。它的名字来源于其发明…

【Unity教程】使用 Animation Rigging实现IK制作程序化的动画

在 Unity 开发中&#xff0c;为角色创建逼真且自适应的动画是提升游戏体验的关键。在本教程中&#xff0c;我们将结合 Animation Rigging 工具和 IK&#xff08;Inverse Kinematics&#xff0c;反向运动学&#xff09;插件来实现程序化的动画。 视频教程可以参考b战大佬的视频 …

不只是翻译,更是智慧碰撞!有道翻译与3大强敌的全方位较量

现在的科技牛得不行&#xff0c;翻译软件早就成了咱们学习、工作、生活里少不了的帮手。它们不光是把语言的墙给推倒了&#xff0c;还让咱们说话交流快多了。今儿个&#xff0c;咱们就一块儿瞧瞧2024年的翻译软件圈子&#xff0c;瞅瞅那几个最火的翻译工具&#xff0c;它们怎么…

生信软件30 - 快速单倍型分析工具merlin

Merlin可用于连锁分析或关联分析、IBD和亲缘关系估计、单倍型分析、错误检测和模拟。 1. merlin下载 下载地址&#xff1a; http://csg.sph.umich.edu/abecasis/merlin/download/ # linux版本 wget http://csg.sph.umich.edu/abecasis/merlin/download/Linux-merlin.tar.gz …

如何在本地和远程删除 Git 分支?

如何在本地和远程删除 Git 分支&#xff1f; 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职于医疗科技公司&#xff0c;热衷分享知识&#xff0c;武汉城市开发者社区主理人 擅长.n…

创新赛场的制胜法宝:如何让你的商业计划书脱颖而出

创新赛场的制胜法宝&#xff1a;如何让你的商业计划书脱颖而出 前言明确产品或服务的核心功能突出创新性用户需求和市场痛点具体示例和场景详细描述产品和服务的方法实例化产品与服务视觉辅助工具未来展望结语 前言 作为一名资深的项目负责人&#xff0c;我有幸参与了无数次创新…

React + Vite项目别名配置

Node版本&#xff1a;v20.16.0Vite版本&#xff1a;5.4.1 安装 types/node 依赖包 pnpm i types/node -D pnpm ls types/node配置 vite.config.js 文件: resolve: {alias: {"": join(__dirname, "./src/"),}, },使用配置好的别名 &#xff1a; 由上图我们…

考试:操作系统知识(02)

进程同步和互斥 临界资源&#xff1a;各进程间需要以互斥方式对其进行访问的资源。 临界区&#xff1a;指进程中对临界资源实施操作的那段程序。本质是一段程序代码。 ◆互斥&#xff1a;某资源(即临界资源) 在同一时间内只能由一个任务单独使用&#xff0c;使用时需要加锁&…

kafka连接图形化工具(Offset Explorer和CMAK)

kafka连接图形化工具 1、Offset Explorer1.1、下载Offset Explorer1.2、安装Offset Explorer1.3、配置Offset Explorer连接kafka 2、CMAK&#xff08;kafka的web管理后台&#xff09;2.1、下载2.2、解压安装2.3、配置2.4、启动2.5、CMAK访问 1、Offset Explorer Offset Explor…

【数据结构】二叉树链式结构(c语言)(附源码)

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;数据结构 目录 前言 一、节点的定义 二、创建一棵二叉树 1. 创建新节点 2. 手动创建二叉树 三、方法的声明 四、方法的实现 1. 前、中、后序遍历 1.1 前…

libLZMA库iOS18平台编译

1.下载xz源码: 使用autogen.sh生成configure文件 2.生成makefile rm -rf ./build/iOS && mkdir -p ./build/iOS && cd ./build/iOS && ../../configure --host=arm-apple-darwin64 --prefix=`pwd`/Frameworks/lzma CC="xcrun -sdk iphoneos cl…

二十天刷leetcode【hot100】算法- day2[前端Typescript]

指针 6.三数之和 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的…

Maven项目中Allure和AspectJ的配置及测试执行(常见报错解决方法)

Maven项目中Allure和AspectJ的配置及测试执行 在自动化测试领域&#xff0c;Allure和AspectJ是两个非常有用的工具。Allure提供了丰富的测试报告&#xff0c;而AspectJ则允许我们以声明式的方式编写横切关注点&#xff0c;如日志记录、事务管理等。本文将指导您如何在Maven项目…

MySQL基础练习题47-判断三角形

目录 题目 准备数据 分析数据 方法一 &#xff1a;if函数 方法二&#xff1a;case when 题目 对每三个线段报告它们是否可以形成一个三角形。 准备数据 ## 创建库 create database db; use db;## 创建表 Create table If Not Exists Triangle (x int, y int, z int)## …