【Linux】进程通信实战 —— 进程池项目

在这里插入图片描述
送给大家一句话:
没有一颗星,会因为追求梦想而受伤,当你真心渴望某样东西时,整个宇宙都会来帮忙。 – 保罗・戈埃罗 《牧羊少年奇幻之旅》

🏕️🏕️🏕️🏕️🏕️🏕️
🗻🗻🗻🗻🗻🗻


进程通信实战 —— 进程池项目

  • 1 ♻️知识回顾
  • 2 ♻️项目介绍
  • 3 ♻️项目实现
    • 3.1 ✨创建信道和子进程
    • 3.2 ✨建立任务
    • 3.3 ✨控制子进程
    • 3.4 ✨回收信道和子进程
  • 4 ♻️总结
  • Thanks♪(・ω・)ノ谢谢阅读!!!
  • 下一篇文章见!!!

1 ♻️知识回顾

在之前的讲解中,我们深入探讨了以下几个方面:

  1. 父子进程的创建与管理:我们详细讲解了父子进程是如何建立的,以及子进程如何继承父进程的代码和数据。子进程通常用于完成特定的任务。
  2. 文件操作:我们学习了如何使用 read 和 write 操作文件,并了解了文件描述符(fd)的概念,从而能够在文件中进行信息的读取和写入。
  3. 进程间通信:我们介绍了匿名管道,这是一种父子进程间进行通信的方式。通过共享资源,父子进程可以实现数据的传递和同步。

在接下来的内容中,让我们把所学知识来进行运用,我们将探讨进程池的概念和实现细节。

2 ♻️项目介绍

进程池是一种用于管理和复用进程的技术,它可以有效地管理系统资源并提高程序的性能和效率。通过维护一组预先创建的进程与管道,进程池可以避免频繁地创建和销毁进程,从而减少了系统开销和资源浪费。
在这里插入图片描述
主要使用的是池化技术的思想:

池化技术是一种广泛应用于系统开发中的优化策略,旨在通过复用资源来提高性能和效率。池化技术的核心思想是预先分配一组资源,并在需要时进行复用,而不是每次都重新创建和销毁资源。

池化技术(Pooling)涉及创建和管理一组预先分配的资源,这些资源可以是进程、线程、数据库连接或对象实例。在池化系统中,当请求到达时,它会从池中获取一个空闲资源,使用完毕后将其归还池中。这种方法避免了频繁的创建和销毁操作,从而显著减少了系统开销。

进程池就是通过预先创建若干个进程与管道,在需要进行任务时,选择一个进程,通过管道发送信息,让其完成工作。

进程池在实际项目中有广泛的应用,尤其是在处理大量并发任务时,例如:网络服务器中的请求处理、数据处理以及计算密集型任务。通过合理配置进程池的大小和参数,可以有效控制系统负载,提高整体响应速度。

3 ♻️项目实现

3.1 ✨创建信道和子进程

首先我们需要建立一个信道类,来储存管道及其对应的子进程信息。

//信道类
class Channel
{
public:Channel(pid_t id , int wfd , std::string name):_id(id) , _wfd(wfd) , _name(name)    {}~Channel(){}void Close(){close(_wfd);}//关闭管道时需要等待对应子进程结束void WaitSub(){pid_t rid = waitpid(_id, nullptr, 0);if (rid > 0){std::cout << "wait " << rid << " success" << std::endl;}}pid_t GetId(){ return _id;}int GetWfd(){ return _wfd;}std::string GetName(){return _name ;}
private:pid_t _id ;//对应 子进程 idint _wfd  ;//写入端std::string _name ; //管道名称
};

然后我们就建立若干个信道与子进程,创建子进程与信道的时候,把信息插入到信道容器中,完成储存。子进程需要阻塞在读取文件,等待父进程写入信息:

void CreateChannel(int num , std::vector<Channel>* channel)
{//初始化任务InitTask();for(int i = 0 ; i < num ; i++){//创建管道int pipefd[2] = {0};int n = pipe(pipefd);if(n != 0){std::cout << "create pipe failed!" << std::endl;}//创建子进程pid_t id = fork();if(id == 0){//子进程 --- 只读不写close(pipefd[1]);work(pipefd[0]);close(pipefd[0]);exit(0);}//父进程close(pipefd[0]);std::string name = "Channel - " + std::to_string(i);//储存信道信息channel->push_back( Channel(id , pipefd[1] , name) );}
}

这里提一下传参的规范:

  • const &:表示输出型参数,即该参数是输入型,不会被修改。常用于传递不需要修改的对象或数据。
  • &:表示输入输出型参数,即该参数既是输入参数,又是输出参数,函数可能修改其内容。
  • * :表示输出型参数,通常用于传递指针,函数通过指针参数返回结果给调用者。

进行一下测试,看看是否可以这正常建立信道与子进程;

int main(int argc , char* argv[])
{//1. 通过main函数的参数 int argc char* argv[] (./ProcessPool 5) //判断要创建多少个进程if(argc != 2){std::cout << "请输入需要创建的信道数量 :"  << std::endl;}std::vector<Channel> channel;int num = std::stoi(argv[1]);//2. 创建信道和子进程CreateChannel(num , &channel);//测试:for(auto t :  channel){std::cout<< "==============="<<std::endl;std::cout<< "信道对应 name :" << t.GetName() <<std::endl;std::cout<< "信道对应子进程 pid :" << t.GetId() <<std::endl;std::cout<< "信道对应写端 wfd :" << t.GetWfd() <<std::endl;}return 0;
}

在这里插入图片描述
完美,可以正常创建!!!

3.2 ✨建立任务

完成了信道与子进程的创建,接下来我们就来设置一些任务。我们在.hpp文件里直接把声明定义写在一起,确保代码的模块化和可维护性。

void Print()
{std::cout << "this is Print()"<< std::endl;
}void Fflush()
{std::cout << "this is Fflush()"<< std::endl;
}void Scanf()
{std::cout << "this is Scanf()"<< std::endl;
}

然后通过函数指针数来储存这些函数,因为子进程会继承父进程的数据,这样通过一个数字下标即可确定调用的函数。只需要传入 4 个字节的int类型,最大程度的减少了通信的成本!!!

#pragma once#include <iostream>
#include <ctime>
#include <cstdlib>
#include <sys/types.h>
#include <unistd.h>#define TaskNum 3
//这个文件里是任务函数
typedef void(*task_t)();task_t tasks[TaskNum];
//...
//...三个函数
//...
void InitTask()
{srand(time(nullptr) ^ getpid() ^ 17777);tasks[0] = Print;tasks[1] = Fflush;tasks[2] = Scanf;
}
//执行任务!!!
void ExecuteTask(int num)
{if(num < 0 || num > 2) return;tasks[num]();
}
//随机挑选一个任务
int SelectTask()
{return rand() % TaskNum;
}

3.3 ✨控制子进程

首先通过 SelectTask() 选择一个任务,然后选择一个信道和子进程。需要注意的是,这里要依次调用每一组子进程,采用轮询(Round-Robin)方案,以尽可能实现负载均衡。 然后发送任务(向信道写入4字节的数组下标)

int SelectChannel(int n)
{//静态变量做到轮询方案static int next = 0;int channel = next;next++;next %= n;return channel;
}
void SendTaskCommond(Channel& channel , int TaskCommand  )
{//写入对应信息write(channel.GetWfd() , &TaskCommand , sizeof(TaskCommand));
}void CtrlProcessOnce(std::vector<Channel>& channel)
{//选择一个任务int TaskCommand = SelectTask();//选择一个进程与信道int ChannelNum = SelectChannel(channel.size());//发送信号//测试std::cout << "taskcommand: " << TaskCommand << " channel: "<< channel[ChannelNum].GetName() << " sub process: " << channel[ChannelNum].GetId() << std::endl; SendTaskCommond(channel[ChannelNum] ,TaskCommand);}

我们写入之后,子进程就可以读取任务并执行,注意子进程读取只读4个字节!!!如果读取的个数不正确,那么就出现了错误,需要报错!!!

//子进程运行函数
void work(int rfd)
{while(true){int Commond = 0;//等待相应int n = read(rfd , &Commond , sizeof(Commond));if(n == sizeof(int)){std::cout << "pid is : " << getpid() << " handler task" << std::endl;//执行命令std::cout << "commond :" << Commond << std::endl;ExecuteTask(Commond);}//写端关闭else if(n == 0){std::cout << "sub Process:" << getpid() << std::endl;break;}}
}
//...//创建子进程pid_t id = fork();if(id == 0){//子进程 --- 只读不写close(pipefd[1]);work(pipefd[0]);close(pipefd[0]);exit(0);}
//...

进行一下测试:
在这里插入图片描述
成功执行任务!!!

3.4 ✨回收信道和子进程

首先关闭信道写端,这样子进程会自己退出,然后父进程等待子进程退出(wait等待子进程 )不要出现僵尸进程 !!!

注意由于子进程会继承父进程的数据,所以一个信道实际上会有多个写端。为了不必要的错误,分开集中操作:先关闭所有写端,再等待所以子进程。

void CleanUpChannel(std::vector<Channel>& channel)
{for(auto t : channel){t.Close();}for(auto t : channel){t.WaitSub();}
}

测试一下:
在这里插入图片描述
5 个子进程成功退出释放!!!

4 ♻️总结

这样,我们的进程池项目就完成了。不过,实际上我们还可以进一步优化,比如优化 work 函数,将其设置为回调函数,以实现完全解耦。

尽管如此,目前的实现已经能够满足我们的项目需求。一个面向过程的进程池项目就此完成!!!

Thanks♪(・ω・)ノ谢谢阅读!!!

下一篇文章见!!!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Stream流的使用

目录 一&#xff0c;Stream流 1.1 概述 1.2 Stream代码示例 二&#xff0c;Stream流的使用 2.1 数据准备 2.2 创建流对象 2.3 中间操作 filter map distinct sorted limit skip flatMap 2.4 终结操作 foreach count max&min collect 2.5 查找与匹配 a…

免费图片文字转换成文本,ocr文字识别软件免费版,真的太实用了!

截屏短视频上一段扎心文字&#xff0c;想把它发到朋友圈怎么办&#xff1f;这时候就需要一个OCR识别软件。 它就像一个聪明的小助手&#xff0c;它可以帮助电脑“看懂”书本上或者图片里的字。就像我们用眼睛看字一样&#xff0c;OCR软件用它的“眼睛”扫描图片&#xff0c;识…

【组合数学】常考试题答案

一、单项选择题&#xff08;每小题3分&#xff0c;共15分&#xff09; 1. 用3个“1”和4个“0”能组成&#xff08; &#xff09;个不同的二进制数字。 A. 35 B. 36, C. 37, D. 38 2. 整除300的正整数的个数为&#xff08;  &#xff09;。 A. 14…

Java进阶学习笔记27——StringBuilder、StringBuffer

StringBuilder&#xff1a; StringBuilder代表可变字符串对象&#xff0c;相当于一个容器&#xff0c;它里面装的字符串是可以改变的&#xff0c;就是用来操作字符串的。 好处&#xff1a; StringBuilder比String更适合做字符串的修改操作&#xff0c;效率会更高&#xff0c;…

小白入职 必要熟悉 Git / tortoiseGit 工具

1.安装Git 1.1 了解Git Git是分布式版本控制系统&#xff0c;没有中央服务器的每个人的电脑就是一个完整的版本库&#xff0c;工作时无需联网可多人协作&#xff0c;只需把各自的修改推送给对方&#xff0c;就可以互相看到对方的修改了 分布式版本控制工具管理方式&#xff…

AI崛起,掌握它,开启智能新生活!

AI崛起&#xff0c;掌握它&#xff0c;开启智能新生活&#xff01; &#x1f604;生命不息&#xff0c;写作不止 &#x1f525; 继续踏上学习之路&#xff0c;学之分享笔记 &#x1f44a; 总有一天我也能像各位大佬一样 &#x1f3c6; 博客首页 怒放吧德德 To记录领地 &…

算法:树状数组

文章目录 面试题 10.10. 数字流的秩327. 区间和的个数315. 计算右侧小于当前元素的个数 树状数组可以理解一种数的存储格式。 面试题 10.10. 数字流的秩 假设你正在读取一串整数。每隔一段时间&#xff0c;你希望能找出数字 x 的秩(小于或等于 x 的值的个数)。 请实现数据结构…

H5扫描二维码相关实现

H5 Web网页实现扫一扫识别解析二维码&#xff0c;就现在方法的npm包就能实现&#xff0c;在这个过程中使用过html5-qrcode 和 vue3-qr-reader。 1、html5-qrcode的使用 感觉html5-qrcode有点小坑&#xff0c;在使用的时候识别不成功还总是进入到错误回调中出现类似NotFoundExc…

mongoengine,一个非常实用的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个超酷的 Python 库 - mongoengine。 Github地址&#xff1a;https://github.com/MongoEngine/mongoengine 在现代应用程序开发中&#xff0c;NoSQL数据库因其灵活性和高性能而广受欢迎。MongoD…

论文精读--InstructGPT

模型效果取决于数据效果&#xff0c;但在精细度上控制不够&#xff0c;只是大力出奇迹&#xff0c;这样有很大的问题&#xff1a; &#xff08;1&#xff09;数据量太多或者没有这方面的数据&#xff0c;模型学不会怎么办 &#xff08;2&#xff09;安全性问题&#xff0c;模…

制作电子画册速成攻略,快来试试

​当今社会&#xff0c;数字媒体日益普及&#xff0c;电子画册作为一种崭新的展示方式&#xff0c;受到了越来越多人的青睐。它不仅形式新颖&#xff0c;互动性强&#xff0c;而且制作起来也并不复杂。想知道如何快速掌握制作电子画册的技巧吗&#xff1f;我来教你吧。 接下来&…

1-Django开端--学生管理系统

目录 项目结构 前端页面: add_data.html class_data.html index.html apps.py models.py views.py settings,py urls.py ...实现简略的身架... 项目结构 前端页面: add_data.html --添加数据. {% extends index/index.html %}{% block content %} <div class&qu…

关于数据库和数据表的基础SQL

目录 一. 数据库的基础SQL 1. 创建数据库 2. 查看当前有哪些数据库 3. 选中数据库 4. 删除数据库 5. 小结 二. 数据表的基础SQL 1. 创建数据表 2. 查看当前数据库中有哪些表 3. 查看指定表的详细情况(查看表的结构) 4. 删除表 5. 小结 一. 数据库的基础SQL 1. 创建…

设计模式八股文

什么是设计模式&#xff1f; 设计模式是软件开发过程中经常遇到的问题的通用解决方案。类似于前人总结的经验&#xff0c;遇到相似问题的时候有个参考。 设计模式七大基本原则&#xff1f; 单一职责&#xff1a;一个类应该只作一件事情。将功能分为小的独立的单元。开放封闭…

springboot3微服务下结合springsecurity的认证授权实现

1. 简介 在微服务架构中&#xff0c;系统被拆分成许多小型、独立的服务&#xff0c;每个服务负责一个功能模块。这种架构风格带来了一系列的优势&#xff0c;如服务的独立性、弹性、可伸缩性等。然而&#xff0c;它也带来了一些挑战&#xff0c;特别是在安全性方面。这时候就体…

来自Java的“菱形继承“,你听说过吗?

一、菱形继承的概念 菱形继承又叫做钻石继承&#xff0c;指的是不同的类同时继承自相同的父类&#xff0c;存在一个子类同时继承这些不同的类&#xff0c;即我们常说的“多继承”问题。 例如&#xff1a;B类和C类分别继承A类&#xff0c;而D类同时继承B类和C类。 如此图所示 二…

专业渗透测试 Phpsploit-Framework(PSF)框架软件小白入门教程(十三)

本系列课程&#xff0c;将重点讲解Phpsploit-Framework框架软件的基础使用&#xff01; 本文章仅提供学习&#xff0c;切勿将其用于不法手段&#xff01; 接上一篇文章内容&#xff0c;讲述如何进行Phpsploit-Framework软件的基础使用和二次开发。 我们&#xff0c;继续讲一…

Unity | 框架MVC

目录 一、MVC介绍 二、搭建UI界面 三、代码实现 1.Model层 2.View层 3.Controller层 四、MVC框架测试 五、知识补充 一、MVC介绍 model&#xff1a;数据层。界面展示的数据&#xff08;需要进行初始化、更新、保存、事件通知等操作&#xff09;&#xff0c;单例模式&am…

React中显示数据

SX 会让你把标签放到 JavaScript 中。而大括号会让你 “回到” JavaScript 中&#xff0c;这样你就可以从你的代码中嵌入一些变量并展示给用户。例如&#xff0c;这将显示 user.name&#xff1a; return (<h1>{user.name}</h1> ); 你还可以将 JSX 属性 “转义到 …

宁夏银川、山东济南、中国最厉害的改名大师的老师颜廷利教授的前沿思想观点

在当代社会&#xff0c;一个响亮的声音穿越了传统的迷雾&#xff0c;它来自东方哲学的殿堂&#xff0c;由一位现代学者颜廷利教授所发出。他的话语&#xff0c;如同一股清泉&#xff0c;在混沌的世界里激荡着思考的波澜&#xff1a;"有‘智’不在年高&#xff0c;无‘智’…