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

19 篇文章 0 订阅

通过创建匿名管道实现一个父进程向多个子进程发送任务,并实现具体任务的模拟操作.
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; //创建对象

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

温有情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值