Linux操作系统——管道(二) 进程池

概念层面理解进程池

比如说我们一开始有一个父进程,分别创建5个管道,5个子进程,这5个子进程都向管道里面进行读取,而我们对应的父进程,因为我们前面谈过管道的4种情况里面,有一个种情况是,正常情况下,如果管道没有数据了,读端必须等待,直到有数据为止(写端写入数据了),也就是说父进程只需要向某一个管道进行写入数据,对应的子进程就会被唤醒然后去读取对应的数据,其实管道里面写入的数据除了字符串还可以是整形,因为管道是面向字节流的,当子进程拿到父进程写入的int数据,就可以根据数据的值做不同的工作了。所以我们把父进程叫做主进程,一个一个的子进程就是相应的工作进程。所以我们规定,通信双方按4字节来读取的话,当子进程还没有唤醒的时候也可以继续给管道里面进行写入数据,然后一旦子进程醒来就读取管道中的数据,我们把管道当作队列来用,这样的话就可以按照写入的顺序来进行读取了。

我们都知道有很多资源创建的时候都要有成本的,目前来说这些成本对于计算机而言无外乎就是空间资源(内存),时间资源,比如说创建进程是需要花费系统的时间和空间的,创建我们的未来的线程都是在系统层面通过系统调用去创建的,可是呢在我们的系统当中有一些任务需要去处理,如果我们是在要去做任务的时候再去创建,那么就会比较耽误时间,如果我们提前把进程创建好,当有任务到来的时候呢,我们直接把任务派发给已经创建好的进程,然后让已经创建好的进程帮我们去完成相应的任务,这样就能够省略了创建进程需要等待的时间,所以我们把提前创建好的这些进程,后续帮我们完成任务的这些进程呢就叫做进程池。说到这里呢,其实我们在之前学的c/c++用到的new ,malloc这种申请内存的关键字是需要系统做很多工作的,那么关于申请内存这方面,我们是一次性100MB内存好还是申请10次10MB的内存好呢?其实当我们使用new和malloc这样申请内存的关键字时,是需要进行系统调用的来申请内存,进而把内存交给我们,而系统调用是对

相当于是什么呢,如果我们只申请一次100MB,那么就是只进行一次系统调用,申请10次10MB就是要进行10次系统调用,那么其实就是说你是希望操作系统帮你干事情干一次还是干十次,虽然我们没有去对其进行验证,但是很明显,申请一次100MB效率会更高,因为这种情况和操作系统交互调用系统调用的次数是比较小的,其实就是想说一个朴素的道理就是,调用系统调用也是有成本的。其实生活当中也有大量的池化技术,只不过这里是把池化技术搬到了我们的计算机当中。

下面我们再用代码来进行实现:

代码层面实现进程池

Makefile代码:

processpool:ProcessPool.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f processpool

ProcessPool.cc代码:

#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "Task.hpp"const int num = 5;
static int number = 1;class channel
{
public:channel(int fd, pid_t id) : ctrlfd(fd), workerid(id){name = "channel-" + std::to_string(number++);}public:int ctrlfd;pid_t workerid;std::string name;
};void Work()
{while (true){int code = 0;ssize_t n = read(0, &code, sizeof(code));if (n == sizeof(code)){if (!init.CheckSafe(code))continue;init.RunTask(code);}else if (n == 0){break;}else{// do nothing}}std::cout << "child quit" << std::endl;
}void PrintFd(const std::vector<int> &fds)
{std::cout << getpid() << " close fds: ";for(auto fd : fds){std::cout << fd << " ";}std::cout << std::endl;
}// 传参形式:
// 1. 输入参数:const &
// 2. 输出参数:*
// 3. 输入输出参数:&
void CreateChannels(std::vector<channel> *c)
{// bugstd::vector<int> old;for (int i = 0; i < num; i++){// 1. 定义并创建管道int pipefd[2];int n = pipe(pipefd);assert(n == 0);(void)n;// 2. 创建进程pid_t id = fork();assert(id != -1);// 3. 构建单向通信信道if (id == 0) // child{if(!old.empty()){for(auto fd : old){close(fd);}PrintFd(old);}close(pipefd[1]);dup2(pipefd[0], 0);Work();exit(0); // 会自动关闭自己打开的所有的fd}// fatherclose(pipefd[0]);c->push_back(channel(pipefd[1], id));old.push_back(pipefd[1]);// childid, pipefd[1]}
}void PrintDebug(const std::vector<channel> &c)
{for (const auto &channel : c){std::cout << channel.name << ", " << channel.ctrlfd << ", " << channel.workerid << std::endl;}
}void SendCommand(const std::vector<channel> &c, bool flag, int num = -1)
{int pos = 0;while (true){// 1. 选择任务int command = init.SelectTask();// 2. 选择信道(进程)const auto &channel = c[pos++];pos %= c.size();// debugstd::cout << "send command " << init.ToDesc(command) << "[" << command << "]"<< " in "<< channel.name << " worker is : " << channel.workerid << std::endl;// 3. 发送任务write(channel.ctrlfd, &command, sizeof(command));// 4. 判断是否要退出if (!flag){num--;if (num <= 0)break;}sleep(1);}std::cout << "SendCommand done..." << std::endl;
}
void ReleaseChannels(std::vector<channel> c)
{// version 2// int num = c.size() - 1;// for (; num >= 0; num--)// {//     close(c[num].ctrlfd);//     waitpid(c[num].workerid, nullptr, 0);// }// version 1for (const auto &channel : c){close(channel.ctrlfd);waitpid(channel.workerid, nullptr, 0);}// for (const auto &channel : c)// {//     pid_t rid = waitpid(channel.workerid, nullptr, 0);//     if (rid == channel.workerid)//     {//         std::cout << "wait child: " << channel.workerid << " success" << std::endl;//     }// }
}
int main()
{std::vector<channel> channels;// 1. 创建信道,创建进程CreateChannels(&channels);// 2. 开始发送任务const bool g_always_loop = true;// SendCommand(channels, g_always_loop);SendCommand(channels, !g_always_loop, 10);// 3. 回收资源,想让子进程退出,并且释放管道,只要关闭写端ReleaseChannels(channels);return 0;
}

Task.hpp代码:

#pragma once#include<iostream>
#include<functional>
#include<ctime>
#include<unistd.h>
#include<vector>
using task_t = std::function<void()>;
//typedef std::function<void()> task_t;
void Download()
{std::cout<<"我是一个下载任务"<<" 处理者 : "<<getpid()<<std::endl;
}void PrintLog()
{std::cout<<"我是一个打印日志的任务"<<" 处理者 : "<<getpid()<<std::endl;
}void PushVideoStream()
{std::cout<<"这是一个推送视频流的任务"<<" 处理者 : "<<getpid()<<std::endl;
}class Init
{
public://任务码const static int g_download_code = 1;const static int g_printlog_code = 2;const static int g_push_videostream_code = 3;//任务集合std::vector<task_t> tasks;
public:Init(){tasks.push_back(Download);tasks.push_back(PrintLog);tasks.push_back(PushVideoStream);}bool CheckSafe(int code){if(code>=0&&code<tasks.size()) return true;else return false;}void RunTask(int code){return tasks[code]();}int  SelectTask(){return rand()% tasks.size();}std::string ToDesc(int code){switch(code){case g_download_code:return "Download";case g_printlog_code:return "PrintLog";case g_push_videostream_code:return "PushVideoStream";default:return "Uknow";}}
};Init init; //定义对象

运行结果:

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

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

相关文章

在PostgreSQL中不开归档?恭喜你!锅你背定了

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

huggingface打不开,解决方法

解决方法 将 https://huggingface.co/hustvl/Vim-tiny替换成 https://hf-mirror.com/hustvl/Vim-tiny文章目录 解决方法摘要YoloV8改进策略&#xff1a;基于分层注意力的FasterViT&#xff0c;让YoloV8实现性能的飞跃YoloV8改进策略&#xff1a;InceptionNext主干替换YoloV8…

JavaScript 基础五 对象

JavaScript 基础五 对象 1. 对象2. 对象使用① 声明语法② 对象有属性和方法组成③ 属性对象属性的增删改查操作 ④ 方法 3. 对象遍历实例 4. 内置对象① 内置对象② 内置对象Math属性方法 引入&#xff1a;保存网站用户信息&#xff0c;比如姓名、年龄、电话号码&#xff0c;用…

element-ui button 仿写 demo

基于上篇 button 源码分享写了一个简单 demo&#xff0c;在写 demo 的过程中&#xff0c;又发现了一个小细节&#xff0c;分享一下&#xff1a; 1、组件部分&#xff1a; <template><buttonclass"yss-button"click"handleClick":class"[ty…

第二十一回 阎婆大闹郓城县 朱仝义释宋公明-FreeBSD Linux 使用Rsync备份

阎婆状告宋江杀死她女儿阎婆惜&#xff0c;知县有意偏袒宋江&#xff0c;只是一味的拷打唐牛儿&#xff0c;但无奈张三张文远说刀子是宋江的&#xff0c;知县不得已差人拿宋江来审问。第一次没见到人&#xff0c;第二次派朱仝雷横两个人去。 朱仝到地窖里找到了躲藏的宋江&…

使用STM32 DMA实现高效数据传输的设计与优化

使用STM32的DMA功能可以有效地实现高效的数据传输。在下面的解释中&#xff0c;我将介绍如何设计和优化使用STM32 DMA进行高效数据传输的方法。同时&#xff0c;我将提供一些示例代码来帮助您理解和实践。 ✅作者简介&#xff1a;热爱科研的嵌入式开发者&#xff0c;修心和技术…

3.霍夫曼求直线原理与代码(python)

一、原理 我们常用的直线方程是&#xff1b; 对这个公式移项后得到&#xff1b; 其实&#xff0c;当确定时&#xff0c;这条直线就确定了。我对霍夫曼求直线的理解是&#xff1a;在一个二维平面上有很多个点&#xff0c;然后对取不同的值时得到不同的直线&#xff0c;查看二维…

产品经理学习-产品运营《海报制作》

如何策划一款优秀的海报 海报是什么&#xff1f; 是一种将文字和图片结合的信息传递形式&#xff1b;其作用和目的是把想传递给用户的信息高效的传递出去&#xff0c;让用户在极短的时间内产生兴趣&#xff0c;进而产生收藏、分享等行为。 海报的类型&#xff1a; 类型 特点 …

苹果家的脸部电脑终于来啦!

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

jdk17新特性—— record关键字(类似于Lombok功能)

目录 一、record关键字的概述1.1、概述1.2、特性 二、record关键字的代码示例2.1、record关键字代码示例2.2、record关键字代码.class文件示例2.3、record关键字代码示例 总结说明 三、record关键字实现密封接口的代码示例3.1、record关键字实现密封接口的代码示例 四、record关…

LSTM进行情感分析

LSTM进行情感分析的复现–pytorch的实现 关于TextCNN的复现参考本文章 TextCNN的复现–pytorch实现 - 知乎 (zhihu.com) 接下来主要是对代码内容的详解&#xff0c;完整代码将在文章末尾给出。 使用的数据集为电影评论数据集&#xff0c;其中正面数据集5000条左右&#xff…

2024美赛数学建模E题思路+代码

文章目录 1 赛题思路2 美赛比赛日期和时间3 赛题类型4 美赛常见数模问题5 建模资料 1 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 2 美赛比赛日期和时间 比赛开始时间&#xff1a;北京时间2024年2月2日&#xff08;周五&#xff…