进程池

2023-11-14

进程池


进程池的使用场景

当我们需要并行的处理大规模任务的时候,需要使用到多进程,多线程技术,比如说服务器处理大量客户端的任务,我在大一的时候写过一个C/S+mysql架构的聊天室,大概是这样处理的,每当有客户端发出连接请求时,服务器accept成功以后就去fork一个进程去处理关于这个客户端的所有请求,经过后来的一系列的学习,这显然是极其不科学的,狂开进程浪费资源且不说,效率也极低。

  • 动态创建进程(或线程)是比较耗费时间的,这将导致较慢的客户响应。
  • 动态创建的子进程(或子线程)通常只用来为一个客户服务(除非我们做特殊处理),这将导致系统上产生大量的细微进程(或者线程)。进程(或者线程)间的切换消费大量CPU时间。
  • 动态创建的子进程是当前进程的完整映像。当前进程必须谨慎地管理其分配的文件描述符和堆内存等系统资源,从而使系统的可用资源急剧下降,进而影响服务器的性能。

池化的思想

为了解决上述问题呀,我们的前辈们相出了用池化的思想来解决这些资源消耗问题。简而言之,就是提前创建好一些进程,一般3-7个(具体看cpu核数以及运行环境),目的是为了充分释放多核芯的性能,实大规模并行。由于服务器的硬件资源“充裕”,那么提高服务器性能的一个很直接的方法就是以空间换时间,即“浪费”服务器的硬件资源,以换取其运行效率。这就是池的概念。池是一组资源的集合,这组资源在服务器启动之初就完全被创建并初始化,这称为静态资源分配。当服务器进入正式运行阶段,即开始处理客户请求的时候,如果它需要相关的资源,就可以直接从池中获取,无需动态分配。很显然,直接从池中取得所需资源比动态分配资源的速度要快得多,因为分配系统资源的系统调用都是很耗时的。当服务器处理完一个客户连接后,可以把相关的资源放回池中,无需执行系统调用来释放资源。从最终效果来看,池相当于服务器管理系统资源的应用设施,它避免了服务器对内核的频繁访问。


进程池概述

进程池中的所有子进程都运行着相同的代码(不调用exev族函数),并具有相同的属性,比如优先级、 PGID 等。

当有新的任务来到时,主进程将通过某种方式选择进程池中的某一个子进程来为之服务。相比于动态创建子进程,选择一个已经存在的子进程的代价显得小得多。至于主进程选择哪个子进程来为新任务服务,则有两种方法:

1)主进程使用某种算法来主动选择子进程。最简单、最常用的算法是随机算法和 Round Robin (轮流算法)。

2)主进程和所有子进程通过一个共享的工作队列来同步,子进程都睡眠在该工作队列上。当有新的任务到来时,主进程将任务添加到工作队列中。这将唤醒正在等待任务的子进程,不过只有一个子进程将获得新任务的“接管权”,它可以从工作队列中取出任务并执行之,而其他子进程将继续睡眠在工作队列上。

当选择好子进程后,主进程还需要使用某种通知机制来告诉目标子进程有新任务需要处理,并传递必要的数据。最简单的方式是,在父进程和子进程之间预先建立好一条管道,然后通过管道来实现所有的进程间通信。在父线程和子线程之间传递数据就要简单的多,因为我们可以把这些数据定义全局的,那么他们本身就是被所有线程共享的。

综合上面的论述,我们将进程池的一般模型描述为下图所示的形式。
在这里插入图片描述


处理多客户

在使用进程池处理多客户任务时,首先考虑的一个问题是:监听socket和连接socket是否都由主进程来统一管理。并发模型,其中半同步/半反应堆模式是由主进程统一管理这两种socket的。而高效的半同步/半异步和领导者/追随者模式,则是由主进程管理所有监听socket,而各个子进程分别管理属于自己的连接socket的。对于前一种情况,主进程接受新的连接以得到连接socket,然后它需要将该socket传递给子进程(对于线程池而言,父线程将socket传递给子线程是很简单的。因为他们可以很容易地共享该socket。但对于进程池而言,必须通过管道传输)。后一种情况的灵活性更大一些,因为子进程可以自己调用accept来接受新的连接,这样该父进程就无须向子进程传递socket。而只需要简单地通知一声:“我检测到新的连接,你来接受它。

长连接,即一个客户的多次请求可以复用一个TCP连接。那么,在设计进程池时还需要考虑:一个客户连接上的所有任务是否始终由一个子进程来处理。如果说客户任务是无状态的,那么我们可以考虑使用不同的进程为该客户不同请求服务。

在这里插入图片描述

半同步/半异步进程池实现

综合前面的讨论,我们可以实现这个进程池,为了避免在父、子进程之间传递文件描述符,我们将接受新连接的操作放到子进程中,很显然,对于这种模式而言,一个客户连接上的所有任务始终是由一个子进程来处理的。


奉上代码 进程池github地址


进程池模板 方便以后复用
#ifndef PROCESSPOOL_H
#define PROCESSPOOL_H

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <iostream>

/*子进程类*/
class process
{
   
public:
    /*以 -1初始化*/
    process() : m_pid( -1 ){
   }

public:
    /*子进程号*/
    pid_t m_pid;
    /*父子进程通信管道*/
    int m_pipefd[2];
};

/*进程池类
 *其模板参数是处理逻辑任务的类
 **/
template< typename T >
class processpool
{
   
    /*将构造函数定义为私有的 因此我们只能通过后边的create静态*/
private:
    processpool( int listenfd, int process_number = 8 );

public:
    /*单例模式 在之后调用到*/
    static processpool< T >* create( int listenfd, int process_number = 8 )
    {
   
        if( !m_instance )
        {
   
            m_instance = new processpool< T >( listenfd, process_number );
        }
        return m_instance;
    }
    
    ~processpool()
    {
   
        delete [] m_sub_process;
    }
    
    /*启动进程池*/
    void run();

private:
    void setup_sig_pipe();
    void run_parent();
    void run_child();

private:
    /*进程允许的最大子进程数*/
    static const int MAX_PROCESS_NUMBER = 16;
    /*每个子进程最多能处理的客户数量*/
    static const int USER_PER_PROCESS = 65536;
    /*epoll 最多能处理的事件数*/
    static const int MAX_EVENT_NUMBER = 10000;
    /*进程池中的进程数*/
    int m_process_number;
    /*进程池在池中的序号 从0开始*/
    int m_idx;
    /*每个进程都有一个epoll内核事件表 用epollfd标识*/
    int m_epollfd;
    /*监听socket*/
    int m_listenfd;
    /*子进程通过stop来决定是否停止*/
    int m_stop;
    /*保存所有的子进程的描述信息*/
    process* m_sub_process;
    /*进程池静态实例*/
    static processpool< T >* m_instance;
};



template< typename T >
processpool< T >* processpool< T >::m_instance = NULL;

/*用于处理信号的管道 实现统一信号源
* 全局 */
static int sig_pipefd[2];

static int setnonblocking( int fd )
{
   
    int old_option = fcntl( fd, F_GETFL );
    int new_option = old_option | O_NONBLOCK;
    fcntl( fd, F_SETFL, new_option );
    return old_option;
}

static void addfd( int epollfd, int fd )
{
   
    epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;
    epoll_ctl( epollfd, EPOLL_CTL_ADD, fd, &event );
    setnonblocking( fd );
}

static void removefd( int epollfd, int fd )
{
   
    epoll_ctl( epollfd, EPOLL_CTL_DEL, fd, 0 );
    close( fd );
}

static void sig_handler( int sig )
{
   
    int save_errno = errno;
    int msg = sig;
    /* 这块为啥得转成char* */
    send( sig_pipefd[1], ( char* )&msg, 1, 0 );
    errno = save_errno;
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

进程池 的相关文章

  • 上采样和下采样层 nn.pixelshuffle and nn.pixelunshuffle

    前言 理论部分后面有空的时候补一下 这里先放代码和简要说明 Downsample 这里先对channel维度降低为原来 1 2 1 2 1 2 然后再对channel维度提升 r
  • 高级JAVA开发 MQ部分

    高级JAVA开发 MQ部分 MQ MQ的作用 为什么要用MQ 常见的MQ的优缺点 使用MQ带来的问题以及处理办法 MQ带来的问题列举 消息重复消费 幂等 问题 消息丢失问题 消息顺序性问题 消息过期丢失 大量积压等问题 如何保证MQ高可用性
  • 斯坦福小镇升级版——AI-Town搭建指南

    导语 8月份斯坦福AI小镇开源之后 引起了 AIGC 领域的强烈反响 但8月份还有另一个同样非常有意义的 AI Agent 的项目开源 a16z主导的 AI Town 本篇文章主要讲解如何搭建该项目 如有英文基础或者对这套技术栈熟悉 可直接

随机推荐

  • 用list列表以行方式创建DataFrame

    DataFrame默认会将一维list作为列处理 需要将一维list转为二维才能实现想要的效果 A 1 2 3 4 5 直接pd DataFrame a 得到的是一个1列5行的df 正确方式是 pd DataFrame A 原文链接 htt
  • 个人博客系统(附源码)

    前面学习了那么多的理论知识 一直比较枯燥 今天就做个小项目 来检验一下前面的学习成果吧 有需要源代码的小伙伴可以来看看 个人博客系统 这个小项目主要是模仿CSDN做的 但是功能还是比较少的 只是写出了一些主要的功能 下面就一起来看看吧 目录
  • Merge AVHD with VHD

    AVHD is a file created when you perform snapshot Once you delete the snapshot and shutdown the Virtual Machine automatic
  • Protobuf类型

    1 基本类型 这些是原始的基本数据类型 用于存储数值和字符串 包括 double 双精度浮点数 float 单精度浮点数 int32 32 位有符号整数 int64 64 位有符号整数 uint32 32 位无符号整数 uint64 64
  • centos7升级gcc10.1.0,gdb9.2

    https www gnu org prep ftp html 先找一个亚洲的镜像站点 选一个站点 打开gcc或者gdb目录 选择自己想要安装的版本 我这里选择的gcc 10 1 0和gdb 9 2 http mirrors nju edu
  • smart bi 学习

    Smartbi 安装 部署 测试 官方文档 数据连接 关系数据库 选择数据连接选关系数据库 配置 连电脑本地的数据库 用户名 密码 root root 端口 3306 ip localhost mysql 数据库选的lyj 1 数据库管理
  • [React]为什么写React组件的时候,需要先引入React?

    React相信各位伙伴都不陌生 那么你的React技术还好吗 来跟我一起重学一遍React 看看有什么知识是你没有记住的呢 一起来查漏补缺下 目录 为什么有的React页面及组件在写的时候需要引入 React 为什么会出现这个问题 Reac
  • Android多屏幕适配-平板

    http blog csdn net qq 27570955 article details 53207600 1 常用单位及其关系 px 像素 inch 英寸 pt 1 72 英寸 dpi 一英寸长的直线上的像素点的数量 即像素密度 不同
  • 软件测试人员分工详情

    最近看了点敏捷测试的东西 看得比较模糊 一方面是因为没有见真实的环境与流程 也许它跟本就没有固定的模式与流程 它就像告诉人们要 勇敢 努力 有的人在勇敢的面对生活 有些人在勇敢的挑战自我 有些人在勇敢的面对失败与挫折 好吧 他们都实现了 勇
  • Vue报错之$nextTick

    今天在生产上面出现了一个问题 我们作为一个整个的项目 我们制作的报账系统是其中一个的子系统 但是现在出现了一个问题 因为我们是共同使用一个前段 而且我们是最先上线的 就导致其他的系统在模仿我们的代码情况 然后他们修改了我们的代码中的公共部分
  • ESP32-土壤湿度传感器

    ESP32 土壤湿度传感器使用 土壤湿度传感器介绍 一 连接传感器引脚 二 使用步骤 1 创建代码 2 保存运行 总结 土壤湿度传感器介绍 提示 土壤湿度传感器 有很多种 我这里用的是电阻式土壤湿度传感器 其原理是 把传感器插入土壤中 不同
  • OpenGL--光源

    OpenGL至少支持8个光源 要查询OpenGL实现支持的光源数 可调用glGetIntegerv 要启用或者禁用光源 分别使用glEnable GL LIGHTi 和glDisable GL LIGHTi 其中i的可能取值为0到GL MA
  • mysql查询所有分类前三的数据

    设计思路 当mysql查询有很多分类时 可能只需要每种分类的前三或者前十的数据 不需要返回所有的结果 所以我们可以给不同种类的数据添加序号 然后通过序号来筛选结果 例 建一张工人工作质量表 用年份和质量来分类 CREATE TABLE wo
  • kali如何使用中文语言包的方法

    kali linux2020 06版如何使用中文语言包 原来kali还需要使用独立的汉化包 现在中文语言包是集成在系统中的 但安装上去默认的还是英文 对于我这种英文欠佳的不太友好 于是 打开终端 输入 sudo dpkg reconfigu
  • angular2 对于DOM元素的获取与操作

    为了能够支持跨平台 angular通过抽象层封装了不同平台的差异 正确操作DOM的方式 用ElementRef和Renderer2 这篇文章将讲述如何使用Renderer2来操作DOM元素 我们可以使用Renderer2对元素的class和
  • 基于改进多目标粒子群算法的配电网储能选址定容——附Matlab代码

    目录 摘要 主要内容 程序思路 储能选址定容优化模型 1 节点电压波动 2 负荷波动 3 储能系统容量 改进的多目标粒子群算法 1 自适应权重 2 交叉变异 3 种群全局最优解的选取 算例分析及结果 本文Matlab代码分享 摘要 以系统节
  • 人工智能:分类算法——朴素贝叶斯、决策树的简单理解与代码实现,SVM、人工神经网络的简单理解

    下文使用代码 链接 pan baidu com s 1sR2bt Iu89M3h 8XMPjEuQ 提取ey3q 分类算法朴素贝叶斯 决策树 SVM 人工神经网络 汽车分类实战 一 实验目的 二 实验的硬件 软件平台 三 实验算法原理 一
  • Linux查看应用的CPU、内存使用情况

    目录 一 jps命令 二 ps命令 三 top命令 四 free命令 五 df命令 查看应用的CPU 内存使用情况 使用jps ps top free df命令查看 一 jps命令 可以列出本机所有java应用程序的进程pid jps op
  • c++11 chrono全面解析(高精度时钟,可达纳秒级别)

    1 精度 时钟节拍 时间精度 template
  • 进程池

    进程池 进程池的使用场景 当我们需要并行的处理大规模任务的时候 需要使用到多进程 多线程技术 比如说服务器处理大量客户端的任务 我在大一的时候写过一个C S mysql架构的聊天室 大概是这样处理的 每当有客户端发出连接请求时 服务器acc