手把手带你实现进程池~~

通过创建匿名管道实现一个父进程向多个子进程发送任务,并实现具体任务的模拟操作.
process.cc

#include <iostream>
#include <unistd.h>
#include <vector>
#include <string>
#include <cassert>
#include <sys/wait.h>
#include <sys/types.h>
#include "task.hpp"

using namespace std;

const int num = 5;
static int number = 1;

class channel
{
public:
    channel(int fd, pid_t id)
        : ctrlfd(fd), workid(id)
    {
        name = "channel-" + to_string(number++);
    }

public:
    int ctrlfd; // 这个文件描述符来控制写端
    pid_t workid;//进程id
    string name;//进程名称
};

// 输入参数: const &
// 输出参数: *
// 输入输出参数: &

void Work()
{
    while (true)
    {
        // 子进程的工作是每次接受从父进程传入的4个字节的数据,当然这边也可以每次传一个字节
        int code = 0;
        ssize_t n = read(0, &code, sizeof(code));
        if (n == sizeof(code)) // 说明读到正常值
        {
            if (!init.CheckSafe(code)) // 检查code合法
            {
                continue;
            }
            init.RunTask(code);
        }
        else if (n == 0) // 读到管道文件结尾
        {
            break;
        }
        else // 读取错误
        {
        }
        // cout << "i am runnning,my pid:" << getpid() << endl;
        // sleep(1);
        //
    }
    cout << "child quit" << endl;
}
void Printfd(const vector<int>& fds)
{
    cout << getpid() << "close fds:";
    for(auto fd : fds)
    {
        cout << fd << " ";
    }
    cout << endl;
}
void CreatChannels(vector<channel> *c)
{
    vector<int> old;
    for (int i = 0; i < num; i++)
    {

        // 1.定义并创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n == 0);
        (void)n;

        // 2.创建进程
        pid_t id = fork();
        assert(id != -1);

        // 3.构建单向信道
        if (id == 0)
        {
            if(!old.empty())
            {
                for(auto fd:old)
                {
                    close(fd);
                }
                Printfd(old);
            }
            // child
            close(pipefd[1]);
            dup2(pipefd[0], 0);
            // TODO
            Work();
            exit(0); // 进程退出会关闭自己打开的所有fd
        }

        // father
        close(pipefd[0]);
        c->push_back(channel(pipefd[1], id)); // 父进程进行管道写入
        old.push_back(pipefd[1]); //将需要关闭的进程的文件描述符记录下来
    }
}
void PrintDebug(const vector<channel> &e)
{
    for (const auto &channel : e)
    {
        cout << channel.name << "," << channel.ctrlfd << "," << channel.workid << endl;
    }
}
void SendCommand(const 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();

        // debug
        // cout << "send command: " << init.ToDesc(command) << "[" << command << "]"
        //      << " in " << channel.name << "work is:" << channel.workid << endl;
        // 3.发送任务
        write(channel.ctrlfd, &command, sizeof(command));

        // 4.判断是否要退出
        if (!flag)
        {
            num--;
            if (num <= 0)
                break;
        }
        sleep(1);
    }
    cout << "SendCommand over......" << endl;
}
// void ProcessExit()
// {
//     exit(0);
// }
void ReleaseChannels(vector<channel> c)
{
    // int num = c.size() - 1;
    // while(num>=0)
    // {
    //     close(c[num].ctrlfd); // 关闭写端
    //     waitpid(c[num--].workid, nullptr, 0);
    // }
    for (const auto &channel : c) // 子进程回收
    {
        // pid_t rid = waitpid(channel.workid, nullptr, 0); //(每一个子进程的pid,文件退出的信号(状态),父进程进行等待的方式)
        // if (rid == channel.workid)
        // {
        //     cout << "wait child" << channel.workid << "success..." << endl;
        //}
        close(channel.ctrlfd); // 关闭写端
        waitpid(channel.workid, nullptr, 0);
    }
}
int main()
{
    vector<channel> channels; // 不推荐指定大小,当指定大小时,会调用指定大小次数的默认构造函数,即调用无参构造函数,还需要提供构造函数的重载,我们提供的构造函数是有参的,不推荐
    // 1. 创建信道,创建进程
    CreatChannels(&channels);
    // PrintDebug(channels);
    // sleep(10);

    // 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 <vector>
#include <unistd.h>
#include <ctime> 
using namespace std;
//using task_t = function<void()>;
typedef function<void()> task_t;
void Download()
{
    cout << "我是一个下载任务" << ",处理者:" << getpid() << endl;
}
void PrintLog()
{
    cout << "我是一个打印日志任务" << ",处理者:" << getpid() << endl;
}
void PushVideoStream()
{

    cout << "我是一个推送视频流任务" << ",处理者:" << getpid() << endl;
}

class Init
{
public:
    // 任务集合
    vector<task_t> tasks; // 将上述的三个函数的功能添加到这个vector里面
    // 任务码
    const static int g_download_code = 0;
    const static int g_printlog_code = 1;
    const static int g_push_videostream_code = 2;
public:
    Init()
    {
        tasks.push_back(Download);
        tasks.push_back(PrintLog);
        tasks.push_back(PushVideoStream);

        srand(time(nullptr) ^ getpid());//随机数种子生成
    }
    bool CheckSafe(int code)//保障传过来的任务码是合法的
    {
        if(code >= 0 && code < tasks.size()) return true;
        else return false;
    }
    void RunTask(int code)
    {
        return tasks[code]();//根据传入的code找到对应的vector,然后每次vector里面是一些方法,然后顺便执行调用

    }
    int SelectTask()
    {
        return rand() % tasks.size();
    }
    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 "UnKnow";
        }
    }
};
Init init; //创建对象

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

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

相关文章

stl容器 string类的基本操作

目录 一.string类的构造 二.string类的输出 1.传统字符串输出 2.通过迭代器进行输出 ​编辑 3.C11标准的范围for输出加auto推导类型 三.string类的各种迭代器 begin(&#xff09;和end() 利用迭代器遍历输出 利用迭代器修改字符串的字符 rbgin()和rend() 利用迭代器遍…

[论文阅读]Adversarial Autoencoders(aae)和代码

In this paper, we propose the “adversarial autoencoder” (AAE), which is a probabilistic autoencoder that uses the recently proposed generative adversarial networks (GAN) to perform variational inference by matching the aggregated posterior of the hidden …

【人工智能基础】RNN实验

一、RNN特性 权重共享 wordi weight bais 持久记忆单元 wordi weightword baisword hi weighth baish 二、公式化表达 ht</sub f(ht - 1, xt) ht tanh(Whhht - 1 Wxhxt) yt Whyht 三、RNN网络正弦波波形预测 环境准备 import numpy as np import torch …

服务器端优化-Redis内存划分和内存配置

6、服务器端优化-Redis内存划分和内存配置 当Redis内存不足时&#xff0c;可能导致Key频繁被删除、响应时间变长、QPS不稳定等问题。当内存使用率达到90%以上时就需要我们警惕&#xff0c;并快速定位到内存占用的原因。 有关碎片问题分析 Redis底层分配并不是这个key有多大&…

PG 全页写

1.什么是全页写 修改一个块的时候&#xff0c;把块读到内存中&#xff0c;commit后,WAL写进程会触发写&#xff0c;把修改的块写到WAL日志文件&#xff0c;如果再往这个块中插入一条数据&#xff0c;数据缓冲区里面的块有两条数据了&#xff0c;再次commit后&#xff0c;PG会把…

图像处理--空域滤波增强(原理)

一、均值滤波 线性滤波算法&#xff0c;采用的主要是邻域平均法。基本思想是使用几个像素灰度的某种平均值来代替一个原来像素的灰度值。可以新建一个MN的窗口以为中心&#xff0c;这个窗口S就是的邻域。假设新的新的像素灰度值为&#xff0c;则计算公式为 1.1 简单平均法 就是…

在excel中,alt+13和alt+10都是什么字符?

1.回车符与换行符 Alt13是回车符&#xff0c;Alt10是换行符。 2.用在microsoft word中 在microsoft office中&#xff0c;回车符 和 换行符 对文本来讲都有换行的作用&#xff0c;但它们并不是同一种符号。下图是在word中两种字符的显示&#xff0c; 当使用 回车符 进行文本…

Ubuntu MATE系统下WPS显示错位

系统&#xff1a;Ubuntu MATE 22.04和24.04&#xff0c;在显示器设置200%放大的情况下&#xff0c;显示错位。 显示器配置&#xff1a; WPS显示错位&#xff1a; 这个问题当前没有找到好的解决方式。 因为4K显示屏设置4K分辨率&#xff0c;图标&#xff0c;字体太小&#xff…

TCP(TCP客户端、服务器如何通信)

一、TCP介绍 TCP的特点&#xff1a; 面向连接的协议&#xff1a;TCP是一种可靠的、面向连接的协议&#xff0c;在通信之前需要建立连接&#xff0c;以确保数据的可靠传输。这意味着在传输数据之前&#xff0c;发送方和接收方之间需要建立一条可靠的连接通道。流式协议&#x…

Spring Cloud架构进化实操:Eureka、Apollo、OpenFeign、Ribbon、Zuul组件

文章目录 前言一、引出二、服务注册与发现2.1 创建Eureka注册中心2.1.1 引入pom依赖2.1.2 配置yaml2.1.3 启动服务21.4 测试访问 2.2 创建服务提供者2.2.1 配置yaml2.2.2 启动服务2.2.3 测试访问 2.3 创建服务消费者2.3.1 服务提供者接口2.3.2 服务消费者调用接口 三、负载均衡…

Docker的私有仓库部署-Harbor

目录 一. Docker原生私有仓库 Registry 1. Registry 的介绍 2. Registry 的部署过程 二. Registry 的升级——Habor 1. Harbor 简介 2. Harbor 特性 3. Harbor 的构成 4. Harbor 部署 4.1 部署 Docker-Compose 服务 4.2 部署 Harbor 服务 4.2.1 下载或上传 Harbor…

18_Scala面向对象编程trait

文章目录 trait1.定义trait2.向类中混入特质2.1没有父类2.2有父类 3.动态混入3.1动态混入查询功能到公司业务中 4.父类&#xff0c;子类&#xff0c;特质初始化优先级5.Scala功能执行顺序6.常用API trait –特质的学习需要类比Java中的接口&#xff0c;源码编译之后就是interf…

三种方法解决:检测到在集成的托管管道模式下不适用的 ASP.NET 设置

几天前配置一个IIS环境的网站时,出现500错误。根据错误提示,很快把问题解决了,现记录一下,希望能帮到遇到同样问题的网友。 问题描述 (点击图片放大) 应用程序“DEFAULT WEB SITE”中的服务器错误Internet Information Services 7.5错误摘要 HTTP 错误 500.24 - Interna…

抓包证书安装到安卓7.0+手机

前言: 首先理解一下,这个不只是证书到浏览器,而是抓包证书到安卓7.0+手机上的文章; 还有一点区分,在浏览器上装的证书,只是让抓包工具可以抓取手机浏览器的包,而不是抓取手机app上的包; 如果你的证书只是简单的在浏览器下进行安装,那么你的手机app是走不了代理网络的…

视频教程下载:为 GPTs 商店构建 10 个 GPTs获得被动收入

欢迎来到 AI 驱动的内容创作新时代 - GPT 商店。这门综合课程是您成为定制和利用 GPT 模型解决多样化应用的专家的路线图。无论你是错过了应用商店革命的初始浪潮还是乘着它取得了成功&#xff0c;这都是你站在下一个重大数字飞跃前沿的机会。 课程模块&#xff1a; - 介绍 Ch…

Dragonfly 拓扑的路由算法

Dragonfly 拓扑的路由算法 1. Dragonfly 上的路由 (1)最小路由(2)非最小路由 2. 评估 Dragonfly 拓扑的路由算法 John Kim, William J. Dally 等人在 2008 年的 ISCA 中提出技术驱动、高度可扩展的 Dragonfly 拓扑。而文章中也提到了 针对 Dragonfly 拓扑的路由算法。本文对…

java-函数式编程-语法

目录 1、函数表现形式 分类 lambda表达式 参数类型可以全写&#xff0c;也可以全不写&#xff0c;但不能一部分写&#xff0c;一部分不写lambda 的省略策略&#xff1a;凡是可推导&#xff0c;都可以省略

【c++算法篇】双指针(上)

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;算法笔记仓 朋友们大家好啊&#xff0c;本篇文章我们来到算法的双指针部分 目录 1.移动零2.复写零3.快乐数4.盛水最多的容器 1.移动零 题目链接&#xff1a;283.移动零 题目描述&#xff1a; 算法…

Python量化炒股的数据信息获取—获取上市公司分红送股数据信息

Python量化炒股的数据信息获取—获取上市公司分红送股数据信息 上市公司分红送股数据&#xff0c;都存放在STK_XR_XD表中&#xff0c;该表保存在finance包中。要查看表中的数据信息&#xff0c;需要使用query()函数。 单击聚宽JoinQuant量化炒股平台中的“策略研究/研究环境”…

微服务---gateway网关

目录 gateway作用 gateway使用 添加依赖 配置yml文件 自定义过滤器 nacos上的gateway的配置文件 我们现在知道了通过nacos注册服务&#xff0c;通过feign实现服务间接口的调用&#xff0c;那对于不同权限的用户访问同一个接口&#xff0c;我们怎么知道他是否具有访问的权…
最新文章