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.mzph.cn/news/663898.shtml

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

相关文章

vcruntime140.dll怎么下载安装,vcruntime140.dll详细安装教程

vcruntime140.dll’&#xff0c;这个看似陌生而又技术性的文件名&#xff0c;实际上是计算机系统中一个至关重要的动态链接库文件。它是Visual C Redistributable运行时组件的一部分&#xff0c;由微软公司开发并维护&#xff0c;对于许多基于Windows操作系统的软件正常运行起着…

在 SQL Server 中使用 SQL 语句查询不同时间范围的数据

在 SQL Server 中&#xff0c;我们经常需要从数据库中检索特定时间范围内的数据。通过合理运用 SQL 语句&#xff0c;我们可以轻松地查询今天、昨天、近7天、近30天、一个月内、上一月、本年和去年的数据。下面是一些示例 SQL 查询&#xff0c;让我们逐一了解。 查询今天的数据…

PEI是聚醚酰亚胺(Polyetherimide)在粘接使用时使用UV胶水的优势有哪些?要注意哪些事项?

在使用UV胶水进行聚醚酰亚胺&#xff08;Polyetherimide&#xff0c;PEI&#xff09;粘接时&#xff0c;有一些优势和注意事项&#xff1a; 优势&#xff1a; 1.快速固化&#xff1a; UV胶水具有快速固化的特性&#xff0c;通过紫外线光源的照射&#xff0c;可以在几秒到几分钟…

Oracle PL/SQL Programming 第5章:Iterative Processing with Loops 读书笔记

总的目录和进度&#xff0c;请参见开始读 Oracle PL/SQL Programming 第6版 本章探讨 PL/SQL 的迭代控制结构&#xff08;也称为循环&#xff09;&#xff0c;它允许您重复执行相同的代码。 PL/SQL 提供了三种不同类型的循环结构&#xff1a; 简单或无限循环FOR 循环&#x…

MQTT环境搭建

本文介绍了MQTT测试环境的搭建过程和测试时使用的命令&#xff0c;对需要使用MQTT协议的相关开发人员有一定的帮助。 MQTT&#xff08;Message Queuing Telemetry Transport&#xff0c;消息队列遥测传输&#xff09;。 MQTT是 ISO 标准&#xff08;ISO/IEC PRF 20922&#xf…

国标GB/T 28181详解:GB/T28181状态信息报送流程

目 录 一、状态信息报送 二、状态信息报送的基本要求 三、命令流程 1、流程图 2、流程描述 四、协议接口 五、产品说明 六、状态信息报送的作用 七、参考 在国标GBT28181中&#xff0c;定义了状态信息报送的流程&#xff0c;当源设备(包括网关、SIP 设备、SIP 客…

ThinkPHP6的助手函数汇总

原文地址 abort(): 抛出 HTTP 异常 1. /** 2. * 抛出 HTTP 异常 3. * param integer|Response $code 状态码 或者 Response 对象实例 4. * param string $message 错误信息 5. * param array $header 参数 6. */ 7. abort($code, string…

关于可变类型和不可变类型的探究

个人猜想&#xff08;很遗憾失败了&#xff09; 在硬盘或者系统中存在一个字符集 如果存在硬盘中&#xff0c;那么硬盘出厂的时候他的字符集所占用的空间就已经确定了。 如果存在于系统的话&#xff0c;硬盘应该在出厂的时候为系统设置一个存储系统字符集的地方。在安装系统…

数据库建模之PowerDesigner创建概念模型

数据模型&#xff08;Data Model&#xff09;是数据特征的抽象&#xff0c;它从抽象层次上描述了系统的静态特征、动态行为和约束条件&#xff0c;为数据库系统的信息表示与操作提供一个抽象的框架。数据模型所描述的内容有三部分&#xff0c;分别是数据结构、数据操作和数据约…

01-16Maven-SpringBoot入门

Maven继承Maven高级SpringSpringBoot入门 Maven 一、概念及功能 概念&#xff1a;Maven是Apache软件基金会组织维护的一款专门为Java项目提供项目构建和依赖管理的工具 1.1作用&#xff1a; 项目构建 构建&#xff1a;是一个将代码从开发阶段到生产阶段的一个过程&#xf…

模拟请求ElasticSearch

Step1 安装chrome的这个插件 Step2 打开插件&#xff0c;GET的json填什么。 在IDEA的debug模式&#xff0c;走到Java代码的searchBuilder&#xff0c; 在这个searchBuilder变量里&#xff0c;对里面query变量点右侧 view按钮&#xff0c; IDEA里会显示出一个json&#xff…

Redis核心技术与实战【学习笔记】 - 16.Redis 缓存异常:缓存和数据库不一致

概述 只要使用 Redis 缓存&#xff0c;就必须面对缓存和数据库的一致性问题。 重要的是&#xff0c;如果数据不一致&#xff0c;那么业务应用从缓存中读取的数据就不是最新数据&#xff0c;这会导致严重的问题。比如说&#xff0c;我们把电商商品的库存信息缓存在 Redis 中&am…

Datax问题记录

1、同步mysql&#xff1a;OS errno 24 - Too many open files 2023-11-20 12:30:04.371 [job-0] ERROR JobContainer - Exception when job run com.alibaba.datax.common.exception.DataXException: Code:[DBUtilErrorCode-07], Description:[读取数据库数据失败. 请检查您的…

1.27马尔科夫链,抽样蒙特卡洛模拟(逆转化方法,接受拒绝矩阵),马尔科夫链蒙特卡洛MCMC,隐马尔科夫(HMM(V算法剪枝优化),NLP)

马尔科夫链 蒙特卡洛法模拟 抽样&#xff0c;逆转换方法 就是说由系统自带的随机函数RANDOM&#xff0c;通过下面这个方法&#xff0c;可以变为对应的随机模拟函数 就是说要实现蒙特卡洛模拟&#xff0c;是要先有一个概率表达式&#xff0c;然后基于这个概率表达式&#xff0…

网络原理TCP/IP(1)

文章目录 端口号UDP协议 在网络通信中&#xff0c;协议非常重要 协议进行了分层 应用层就是对应着应用程序&#xff0c;是程序员打交道最多的这一层&#xff0c;调用系统提供的网络api写出来的代码都是属于应用层的 应用层有很多现成的协议&#xff0c;但是更多的还是程序员需要…

OpenHarmony—Hap包签名工具

概述 为了保证OpenHarmony应用的完整性和来源可靠&#xff0c;在应用构建时需要对应用进行签名。经过签名的应用才能在真机设备上安装、运行、和调试。developtools_hapsigner仓 提供了签名工具的源码&#xff0c;包含密钥对生成、CSR文件生成、证书生成、Profile文件签名、Ha…

C++入门【37-C++ 拷贝构造函数】

拷贝构造函数是一种特殊的构造函数&#xff0c;它在创建对象时&#xff0c;是使用同一类中之前创建的对象来初始化新创建的对象。拷贝构造函数通常用于&#xff1a; 通过使用另一个同类型的对象来初始化新创建的对象。复制对象把它作为参数传递给函数。复制对象&#xff0c;并…

2022美国大学生数学建模(优秀获奖论文)-A题:Power Planning Model: Magic Weapon for Cyclists(续)

目录 5.4.3 Multivariate nonlinear programming 5.5 Model Solving and Evaluation 5.5.1 Model Solution 5.5.2 Model Solution Method Evaluation 6 Application

前端输入框简单实现检测@成员输入

大体逻辑是 给input框添加一个input监听&#xff0c;并判断输入是否为获取当前光标的位置&#xff0c;你输入的肯定在光标之前&#xff0c;且肯定是最后一个input输入的内容换行可以被认为空格&#xff0c;需要进行全局替换判断成功的逻辑分为两部分&#xff0c;前方一般来说是…

EF Core入门例子(以SqLite为数据库)

测试环境&#xff1a; visual studio 2017 .net core 2.1 具体步骤如下&#xff1a; 1 新增名称为EFCoreDemo的.net core控制台程序&#xff0c;版本选择.net core 2.1&#xff0c;项目不能放到带中文的目录下&#xff0c;不然到后面执行Add-Migration命令时会报如下的错误…