进程池大揭秘:打造高效任务处理的 “奶茶工厂”

目录

奶茶店架构初览

员工团队搭建(进程池创建)

 菜单与制作规范(任务定义与管理)

订单派送(任务分发) 

打烊收工(资源回收)

 Work 函数:员工的 “工作日常”

进程池这招为啥这么牛

省资源:员工别老换来换去

干活快:大家一起做奶茶

好管理:老板心里有底

生活里的进程池

服务器:网站背后的 “超级奶茶店”

数据处理:大数据的 “加工车间”

任务调度:分布式系统的 “指挥家”

代码模拟进程池

ProcessPool.hpp 

task.hpp 

 Makefile


诶朋友,想象你走进一家超火爆的奶茶店。店里订单天山上雪花一般的多,要是每个订单都现招一个员工来做,那不乱套了嘛!这时,进程池技术就像这家奶茶店的 “高效秘籍”,今天咱就结合代码,唠唠这个神奇的 “秘籍” 是咋运作的。

奶茶店架构初览

员工团队搭建(进程池创建)
 

代码里有个Create函数,它就好比奶茶店老板在组建员工团队。老板要先确定招多少人,代码里是const int num = 5,这就是要创建 5 个子进程,组成进程池

老板要给员工和自己弄个沟通渠道,代码里通过pipe系统调用创建管道,就像拉了根 “通话线”,专门用来传订单(任务指令)。接着用fork创建子进程,这就好比老板复制出一个个员工,每个员工都有自己的 “工作位”(进程 ID)

在员工这边(子进程),要是之前有别的 “通话线”(旧管道),就得先关掉,然后专心用新的 “通话线” 接收订单。而老板(父进程)则把和员工沟通的 “通话线” 一端(写端文件描述符)存好,方便之后派活。

void Create(vector<channel> *c)
{
    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);
                }
            }
            close(pipefd[1]);
            dup2(pipefd[0], 0);
            Work();
            exit(0); // 会自动关闭自己打开的所有的fd
        }

        // father
        close(pipefd[0]);
        c->push_back(channel(pipefd[1], id));
        old.push_back(pipefd[1]);
        // childid, pipefd[1]
    }
}

 菜单与制作规范(任务定义与管理)

Init类就像是奶茶店的 “菜单与制作规范手册”。它定义了各种任务,像g_download_code(对应下载任务,好比制作珍珠奶茶)、g_printlog_code(打印日志任务,类似记录每天的销售情况)、g_push_videostream_code(推送视频流任务,仿佛在社交媒体上宣传新品)。

“手册” 里还有个tasks向量,存着每个任务具体咋做的方法,就像奶茶配方。构造函数把这些 “配方” 加进去,还像每天开店前摇骰子决定新品一样,用srand(time(nullptr))初始化随机数种子,方便随机选任务来做。

class Init
{
public:
    // 任务码
    const static int g_download_code = 0;
    const static int g_printlog_code = 1;
    const static int g_push_videostream_code = 2;
    // 任务集合
    vector<task_t> tasks;

public:
    Init()
    {
        tasks.push_back(Download);
        tasks.push_back(PrintLog);
        tasks.push_back(PushVideoStream);
        srand(time(nullptr));
    }
    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();
    }
    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;

订单派送(任务分发) 

SendCommand函数是店里的 “订单派送员”。它从 “菜单” 里随机挑个任务(订单),就像顾客随机点奶茶。然后按顺序把任务派给员工(子进程),通过 “通话线”(信道控制文件描述符)把任务代码发过去。每派一个单,还得等会儿,就像做一杯奶茶需要时间,这里是sleep(1)模拟这个间隔。 

void SendCommand(const vector<channel> &c, int count)
{
    int pos = 0;
    while (count--)
    {
        // 1. 选择任务
        int command = init.SelectTask();

        // 2. 选择信道(进程)
        const auto &channel = c[pos++];
        pos %= c.size();

        // 3. 发送任务
        write(channel.ctrlfd, &command, sizeof(command));

        sleep(1);
    }

    cout << "SendCommand done" << endl;
}

打烊收工(资源回收)


ReleaseChannels函数在打烊时登场啦!它就像老板检查店里,先把和员工沟通的 “通话线”(控制文件描述符)关掉,再等员工都完成手头工作下班(用waitpid等待子进程结束),保证店里收拾得干干净净,不留下任何 “尾巴”(避免僵尸进程)。 

void ReleaseChannels(std::vector<channel> c)
{
    int num = c.size() - 1;
    for (; num >= 0; num--)
    {
        close(c[num].ctrlfd);
        waitpid(c[num].workerid, nullptr, 0);
    }
}

 Work 函数:员工的 “工作日常”

Work函数是员工的 “工作日常”。员工一直在那等着订单,通过 “热线”(标准输入,被重定向成管道读端)收订单代码。收到完整订单(数据长度对得上),先检查能不能做(通过init.CheckSafe),能做就开工(init.RunTask)。要是 “热线” 没声了(数据长度为 0,老板关了管道),就下班,打印 “child quit”,结束工作。

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
        }
    }

    cout << "child quit" << endl;
}

进程池这招为啥这么牛

省资源:员工别老换来换去

进程池避免了频繁创建和销毁进程,就像奶茶店不能一会儿招一堆人,一会儿又全辞退,太浪费精力和成本了。有了进程池,员工一直在那,随时能接单干活,系统资源利用率蹭蹭往上涨。

干活快:大家一起做奶茶

多个子进程(员工)能同时处理不同任务(订单),就像奶茶店多个员工同时做不同口味的奶茶,大大加快了任务处理速度,顾客不用等太久,系统整体性能就上去了。

好管理:老板心里有底

通过进程池,能轻松控制同时干活的人数(并发度)。就像奶茶店老板知道店里最多能容下多少员工同时工作,不会因为人太多乱糟糟,也不会资源耗尽,保证店里稳稳当当运营。

生活里的进程池

服务器:网站背后的 “超级奶茶店”

在服务器端编程里,进程池超有用。比如网站像个超大型奶茶店,每天有成千上万个顾客(客户端)来访问,发请求(订单)。进程池能让服务器高效处理这些请求,就像奶茶店快速做出一杯杯奶茶,让用户体验超棒。

数据处理:大数据的 “加工车间”

处理大规模数据时,进程池就像大数据的 “加工车间”。比如要分析海量用户数据,或者处理一堆图片,把任务分给不同进程并行处理,就像奶茶店不同员工同时做不同口味的奶茶,速度快得飞起。

任务调度:分布式系统的 “指挥家”

在分布式系统里,进程池像个 “指挥家”。把任务合理分配到不同节点(就像把订单分给不同员工),让整个系统和谐运转,就像奶茶店每个员工都清楚自己该干啥,店里忙而不乱。

代码模拟进程池

ProcessPool.hpp 

#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.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), workerid(id)
    {
        name = "channel-" + to_string(number++);
    }

public:
    int ctrlfd;
    pid_t workerid;
    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
        }
    }

    cout << "child quit" << endl;
}

void Create(vector<channel> *c)
{
    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);
                }
            }
            close(pipefd[1]);
            dup2(pipefd[0], 0);
            Work();
            exit(0); // 会自动关闭自己打开的所有的fd
        }

        // father
        close(pipefd[0]);
        c->push_back(channel(pipefd[1], id));
        old.push_back(pipefd[1]);
        // childid, pipefd[1]
    }
}

// 发送任务
void SendCommand(const vector<channel> &c, int count)
{
    int pos = 0;
    while (count--)
    {
        // 1. 选择任务
        int command = init.SelectTask();

        // 2. 选择信道(进程)
        const auto &channel = c[pos++];
        pos %= c.size();

        // 3. 发送任务
        write(channel.ctrlfd, &command, sizeof(command));

        sleep(1);
    }

    cout << "SendCommand done" << endl;
}

// 回收资源
void ReleaseChannels(std::vector<channel> c)
{
    int num = c.size() - 1;
    for (; num >= 0; num--)
    {
        close(c[num].ctrlfd);
        waitpid(c[num].workerid, nullptr, 0);
    }
}

int main()
{
    vector<channel> channels;

    // 1. 创建进程
    Create(&channels);

    // 2. 发送任务
    SendCommand(channels, 10);

    // 3. 回收资源
    ReleaseChannels(channels);

    return 0;
}

task.hpp 

#pragma once
#include <iostream>
#include <functional>
#include <vector>
#include <ctime>
#include <unistd.h>
using namespace std;

typedef function<void()> task_t;

void Download()
{
    cout << "this is download" << endl;
}

void PrintLog()
{
    cout << "this is PrintLog" << endl;
}

void PushVideoStream()
{
    cout << "this is pushvideo" << endl;
}

class Init
{
public:
    // 任务码
    const static int g_download_code = 0;
    const static int g_printlog_code = 1;
    const static int g_push_videostream_code = 2;
    // 任务集合
    vector<task_t> tasks;

public:
    Init()
    {
        tasks.push_back(Download);
        tasks.push_back(PrintLog);
        tasks.push_back(PushVideoStream);
        srand(time(nullptr));
    }
    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();
    }
    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;

 Makefile

ProcessPool:ProcessPool.cc
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -rf ProcessPool

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

陈大大陈

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

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

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

打赏作者

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

抵扣说明:

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

余额充值