Linux:进程池制作(基于匿名管道和命名管道两个版本)

Linux:进程池制作 & 匿名管道 & 命名管道

  • 前言
  • 一、匿名管道制作进程池
  • 一、进程池框架
  • 二、创建管道、创建进程、工作进程执行任务
    • 2.1 创建管道、创建进程
  • 2.2 工作进程执行任务
  • 三、主进程向子进程发送任务
    • 3.1 任务封装
    • 3.2 主进程向子进程发送任务
  • 四、回收资源
  • 五、所有源代码
    • 5.1源码
    • 5.2 运行结果(包含调试信息)
  • 六、命名管道实现进程池
    • 6.1 实现思路
    • 6.2 源码

前言

 创建进程是有时间成本的。当计算机要执行任务时才创建进程,势必会影响执行任务的性能。所以我们可以通过提前创建一批进程,当有任务需要被执行时直接喂给这些进程即可。我们把这些提前创建好的进程称为进程池!!
 下面我们会通过一个主进程(父进程)通过匿名管道和一批工作进程(子进程)进行通信。父进程通过不断派发任务给子进程,子进程通过读取管道文件中的任务码来执行对应的任务,从而模拟进程池的整个行为!!

一、匿名管道制作进程池

一、进程池框架

 父进程创建一批子进程,并建立单向通信通道。我们这里规定父进程向匿名管道中一次只能写4字节数据,子进程一次只能从管道文件中读取4字节数据。由于匿名管道的性质,父进程只需向目标子进程所对应的管道中发送任务码即可。如果管道文件中存在数据,子进程会不断读取任务码,执行对应的任务;否则子进程进入阻塞状态,等待主进程向管道中写入数据!

 但主进程需要知道向那个管道中发送任务码,向那个匿名管道中进行写入,以及子进程信息。即我们需要对管道进行管理!先描述,在组织!这里我们通过一个类对管道进行描述,其中保存着:主进程控制写端文件描述符、工作进程id和名字!由于我们需要执行快速随机访问,所以选择vector进行组织管理!所以这个进程池的制作大致框架如下:

【描述结构体】:

#define NUM 5//任务进程个数

int number = 1;//管道编号
class channel
{
public:
    channel(int fd, pid_t id)
    :ctrlfd(fd)
    ,workerid(id)
    {
        name = "channle-" + std::to_string(number++);
    }
    
public:
    int ctrlfd;
    pid_t workerid;
    std::string name;
};

【框架】:

int main()
{
    std::vector<channel> channels;//用于管理管道
    
    //1、创建管道,创建进程,工作进程工作方式
    CreaterChannel(&channels);
    
    //2、主进程向工作进程发送任务
    // 这里特殊设计,我们通过g_always_loop来判断主进程是一直发送任务,还是发送指定次后就结束退出
    const bool g_always_loop = true;
    SendTask(channels, !g_always_loop, 10);
    
    // 回收资源: 关闭写端,子进程自动退出
    ReleaseChannels(channels);
    return 0;
}

二、创建管道、创建进程、工作进程执行任务

2.1 创建管道、创建进程

 我们可以通过父进程循环NUM次,每次先创建管道,然后创建子进程。此时关闭父进程和子进程中不需要的读写段,建立单向通信通道。此时就可以建立如下关系:
在这里插入图片描述
 但上述简单关闭管道文件的读写段会存在问题的!我们需要特殊处理:
在这里插入图片描述
 上述这些多余的指向管道读端是fork创建子进程时,子进程继承父进程的信息之一(红线)!所以我们每次创建出的子进程还需将所有继承父进程多余的读端全部关闭,否则无法回收子进程导致内存泄漏!!
 其中最简单的解决办法就是:我们将父进程的写端文件描述符全部记录下来,每次创建出子进程时,子进程所继承的多余读写信息已经全部保存。我们只需依次将其关闭即可!!

void CreaterChannel(std::vector<channel> *channels)
{
    std::vector<int> old;//记录子进程继承的多余读端
    for(int i = 0; i < NUM; i++)//创建NUM个工作进程
    {
        // 1. 创建管道
        int pipefd[2];
        int n = pipe(pipefd);
        assert(n == 0);
        (void)n;//防止编译器报警

        //2. 创建子进程
        pid_t id = fork();
        if(id < 0)
        {
            perror("fork");
            return;
        }

        // 3. 形成单向通信
        if(id == 0)//子进程,工作进程
        {
            if(!old.empty())//将子进程继承父进程的多余读端全部关闭
            {
                for(auto rfd : old)
                {
                    close(rfd);
                }
            }
            close(pipefd[1]);
            dup2(pipefd[0], 0);//将读端重定向到标准输入
            work();//子进程执行任务
            exit(0);
        }

        //父进程,主进程
        close(pipefd[0]);
        channels->push_back(channel(pipefd[1], id));//主进程对应写端和工作进程信息
        old.push_back(pipefd[1]);
    }
}

2.2 工作进程执行任务

 当管道中有数据时,子进程只需读取相关任务码,然后不断执行即可!但如果此时父进程退出时,由于匿名管道的特性,read的返回值会被设为0,此时子进程在进行读取就没有任何意义了,子进程退出!!(检查任务码和执行任务码实现后续会统一分析)

void work()
{
    while(1)
    {
        int code = 0;
        ssize_t n = read(0, &code, sizeof(code));
        if(n == 0)
        {//主进程写端退出,子进程也退出
            break;
        }
        else if(n > 0)
        {
            if(!init.CheckSafe(code))//检查任务码是否合法
                continue;
            init.RunTask(code);//执行任务码
        }
        else
        {
            //nothing
        }
    }
}

三、主进程向子进程发送任务

3.1 任务封装

 下面我们仅仅通过一些打印数据来子进程待执行的所有模拟任务

【待执行任务】:

void Download()
{
    std::cout << "我是一个下载任务" << std::endl;
}


void Printflog()
{
    std::cout << "我是一个打印日志任务" << std::endl;
}

void PushVideoStream()
{
    std::cout << "我是一个推送视频流任务" << std::endl;
}

【任务封装】:
 我们通过包装器function将上述指针函数进行统一。同时我们向管道中读取和写入的是任务码,所以下面我们给出了相应的任务码,并将上述如何通过vector容器进行管理,下标对应任务码信息,并封装成了类!
 除此之外,还提供选择任务接口(随机选择)、检查任务码是否合理、任务码转对应任务名、运行特定任务码对应任务等接口!
 具体如下:

using task_t = std::function<void()>;

class Init
{
public:
    //任务码
    static const int g_Download_code = 0;
    static const int g_Printflog_code = 1;
    static const int g_PushVideoStream_code = 2;
    std::vector<task_t> tasks;
public:
    Init()
    {
        tasks.push_back(Download);
        tasks.push_back(Printflog);
        tasks.push_back(PushVideoStream);
        srand(time(nullptr));
    }

    int SelectTask()//选择任务接口
    {
        return rand() % tasks.size();
    }

    std::string CodeToName(int code)//任务码转对应任务名
    {
        switch(code)
        {
        case 0:
            return "Download";
        case 1:
            return "Printflog";
        case 2:
            return "PushVideoStream";
        default:
            return "Nothing";
        }
    }


    bool CheckSafe(int code)//检查任务码是否合理
    {
        return code >= 0 && code < tasks.size();
    }

    void RunTask(int code)//行特定任务码对应任务
    {
        tasks[code]();
    }
};

Init init;

3.2 主进程向子进程发送任务

 考虑到子进程完成任务的负载均衡,我们通过循环依次向每一个子进程发送任务码,较为平均的将任务分配给子进程!

 向子进程发送任务码,首先要确定待发送的任务,信道。然后将任务码写入信道!
 前面已经提到过,我们这里所设计的发送任务接口支持:死循环一直发送任务、发送指定次数任务后退出!所以最后我们需要判断是否发送任务需求结束,退出!具体如下:

void SendTask(const std::vector<channel> &channels, bool flag, int num)
{
	int pos = 0;//所选择信道所在数组位置下标
    while(true)
    {
        // 1. 选择任务
        int commit = init.SelectTask();
        if(init.CheckSafe(commit))
        {
            // 2. 选择信道, 发送任务码
            channel c = channels[pos++];
            pos %= channels.size();
            write(c.ctrlfd, &commit, sizeof(commit));
        }

        //判断是否需要退出
        if(!flag)
        {
            if(--num == 0)
                break;
        }
    }
}

四、回收资源

 当父进程发送任务信息退出后,我们仅需将写端关闭即可此时子进程执行read的返回值为0。子进程此时就能识别到写端已经退出,此时子进程也退出!最后让父进程等待子进程,防止子进程僵尸导致内存泄漏即可!!

void ReleaseChannels(std::vector<channel> &channels)
{
    for(auto & c : channels)
    {
        close(c.ctrlfd);
        pid_t rid = waitpid(c.workerid, nullptr, 0);
        if(rid == -1)
        {
            perror("waitpid");
            return;
        }
        std::cout << "wait " << c.name << " " << c.workerid << " success" << std::endl;
    }
}

五、所有源代码

emsp;到处为止,进程池的简单制作到处就结束了,下面是所有源码,其中还包含调试代码!

5.1源码

【ProcessPool.chh】

#pragma once 

#include <iostream>
#include <functional>
#include <vector>
#include <ctime>

using task_t = std::function<void()>;

void Download()
{
    std::cout << "我是一个下载任务" << std::endl;
}


void Printflog()
{
    std::cout << "我是一个打印日志任务" << std::endl;
}

void PushVideoStream()
{
    std::cout << "我是一个推送视频流任务" << std::endl;
}


class Init
{
public:
    //任务码
    static const int g_Download_code = 0;
    static const int g_Printflog_code = 1;
    static const int g_PushVideoStream_code = 2;
    std::vector<task_t> tasks;
public:
    Init()
    {
        tasks.push_back(Download);
        tasks.push_back(Printflog);
        tasks.push_back(PushVideoStream);
        srand(time(nullptr));
    }

    int SelectTask()
    {
        return rand() % tasks.size();
    }

    std::string CodeToName(int code)
    {
        switch(code)
        {
        case 0:
            return "Download";
        case 1:
            return "Printflog";
        case 2:
            return "PushVideoStream";
        default:
            return "Nothing";
        }
    }


    bool CheckSafe(int code)
    {
        return code >= 0 && code < tasks.size();
    }

    void RunTask(int code)
    {
        tasks[code]();
    }
};

Init init;

【processPool.cpp】

#include <iostream>
#include <unistd.h>
#include <cassert>
#include <cerrno>
#include <cstdlib>
#include <string>
#include <vector>
#include <cstdio>
 #include <sys/wait.h>
#include "ProcessPool.chh"

#define NUM 5//任务进程个数

int number = 1;//管道编号
class channel
{
public:
    channel(int fd, pid_t id)
    :ctrlfd(fd)
    ,workerid(id)
    {
        name = "channle-" + std::to_string(number++);
    }
    
public:
    int ctrlfd;
    pid_t workerid;
    std::string name;
};

void work()
{
    while(1)
    {
        int code = 0;
        ssize_t n = read(0, &code, sizeof(code));
        if(n == 0)
        {//主进程写端退出,子进程也退出
            break;
        }
        else if(n > 0)
        {
            if(!init.CheckSafe(code))
                continue;
            init.RunTask(code);
        }
        else
        {
            //nothing
        }
    }
}

void CreaterChannel(std::vector<channel> *channels)
{
    std::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();
        if(id < 0)
        {
            perror("fork");
            return;
        }

        // 3. 形成单向通信
        if(id == 0)//子进程,工作进程
        {
            if(!old.empty())//将子进程继承父进程多余读端全部关闭
            {
                for(auto rfd : old)
                {
                    close(rfd);
                }
                for(auto id : old)
                {
                    std::cout << "creater quit id:" << id << " " << std::endl;
                }
            }
            close(pipefd[1]);
            dup2(pipefd[0], 0);//将读端重定向到标准输入
            work();
            exit(0);
        }

        //父进程,主进程
        close(pipefd[0]);
        channels->push_back(channel(pipefd[1], id));
        old.push_back(pipefd[1]);
    }
}

//Debug
void PrintChannel(const std::vector<channel> &channels)
{
    std::cout << channels.size() << std::endl;
    for(int i = 0; i < channels.size(); i++)
    {
        std::cout << "name:" << channels[i].name << ", 写端描述符:" << channels[i].ctrlfd 
                  <<", 工作进程id:" << channels[i].workerid << std::endl;
    }
    std::cout << "Creater success" << std::endl;
}


void SendTask(const std::vector<channel> &channels, bool flag, int num)
{
    int pos = 0;//所选择信道所在数组位置下标
    while(true)
    {
        // 1. 选择任务,返回任务码
        int commit = init.SelectTask();
        if(init.CheckSafe(commit))
        {
            // 2. 选择信道, 发送任务码
            channel c = channels[pos++];
            pos %= channels.size();
            write(c.ctrlfd, &commit, sizeof(commit));

            // Debug
            std::cout << "select channel: " << c.name << ", send task:" << init.CodeToName(commit) << "[" << commit << "]"
                      << ", workerid:" << c.workerid << std::endl;
        }

        //判断是否需要退出
        if(!flag)
        {
            if(--num == 0)
                break;
        }

    }
}

void ReleaseChannels(std::vector<channel> &channels)
{
    for(auto & c : channels)
    {
        close(c.ctrlfd);
        pid_t rid = waitpid(c.workerid, nullptr, 0);
        if(rid == -1)
        {
            perror("waitpid");
            return;
        }
        std::cout << "wait " << c.name << " " << c.workerid << " success" << std::endl;
    }
}

int main()
{
    std::vector<channel> channels;
    //创建管道,创建进程
    CreaterChannel(&channels);
    std::cout << "Creater end" << std::endl;
    PrintChannel(channels);

    //主进程向工作进程发送任务
    const bool g_always_loop = true;
    SendTask(channels, !g_always_loop, 10);
    
    // 回收资源: 关闭写端,子进程自动退出
    //sleep(3);
    ReleaseChannels(channels);
    return 0;
}

5.2 运行结果(包含调试信息)

在这里插入图片描述

六、命名管道实现进程池

6.1 实现思路

 命名管道实现进程池,我们可以创建多个命名管道,同时父进程打开写端,子进程打开读端!(注意此时创建的子进程还是会出现基础多余的读端文件描述符。所以子进程在打开命名管道读端前,同样需要先将多余的写端描述符关闭)
 发生任务码,和子进程执行任务和匿名管道实现方式一样,就不多说了。
 至于回收资源,那就更简单了。我们只需将命名管道写端描述符关闭,此时子进程会获取的读端退出信息(即read返回值为0),将管道读端也关闭!!最后父进程等待回收子进程,防止子进程僵尸即可!!

6.2 源码

【ProcessPool.cpp】

#include <iostream>
#include <unistd.h>
#include <cassert>
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <string>
#include <vector>
#include <cstdio>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "ProcessPool.chh"

#define NUM 5//任务进程个数


class fifo
{
public:
    fifo(std::string str, pid_t id, int fd)
    :FILENAME(str.c_str())
    ,workerid(id)
    ,ctrlfd(fd)
    { }
    
public:
    const char *FILENAME;
    pid_t workerid;
    int ctrlfd;
};

int Work()
{
    int code = 0;
    while(true)
    {
        ssize_t n = read(0, &code, sizeof(code));
        std::cout << "read size:" << n << std::endl;
        if(n == 0)
        {
            std::cout << "write quit, mee to!" << std::endl;
            break;
        }
        else if(n > 0)
        {
            init.RunTask(code);
        }
        else
        {
            std::cerr << "errno: " << errno << "errstring: " << strerror(errno) << std::endl;
            return 1;
        }
    }
    return 0;
}

bool Createrfifo(std::vector<fifo> *fifos)
{
    std::vector<int> old;
    for(int i = 1; i <= NUM; i++)
    {
        std::string filename = "fifo-" + std::to_string(i);
        int s = mkfifo(filename.c_str(), 0644);
        if(s == -1)
        { 
            std::cout << "errno:" << errno << " strerror" << strerror(errno) << std::endl;
            return false;
        }

        pid_t id = fork();
        if(id == 0)//child
        {
            if(!old.empty())
            {
                for(auto& fd : old)
                    close(fd);
            }
            int rfd = open(filename.c_str(), O_RDONLY);
            dup2(rfd, 0);
            Work();
            std::cout << "read close!!!!!!!!!!!!!!!!!!!!!" << std::endl;
            close(rfd);
            exit(0);
        }

        //parent
        int wfd = open(filename.c_str(), O_WRONLY);
        fifos->push_back(fifo(filename, id, wfd));
        old.push_back(wfd);
    }
    return true;
}

void SendCommit(const std::vector<fifo> &fifos, int flag, int num)
{
    int pos = 0;
    while(true)
    {
        // 1. 选择任务
        int code = init.SelectTask();
        if(init.CheckSafe(code))
        {
            //2. 发送任务码
            write(fifos[pos].ctrlfd, &code, sizeof(code));
            pos = (pos + 1) % fifos.size();
        }
        // 3.判断是否退出
        if(!flag)
        {
            if(--num == 0)
            {
                break;
            }
        }
    }
    std::cout << "write finish!!!" << std::endl;
}

void Releasefifo(std::vector<fifo> &fifos)
{
    std::cout << "start release" << std::endl;
    for(auto& fifo : fifos)
    {
        std::cout << "colse wfd: " <<  fifo.ctrlfd << std::endl;
        close(fifo.ctrlfd);
        pid_t id = waitpid(fifo.workerid, nullptr, 0);
    }
    std::cout << "release success!" << std::endl;
}

int main()
{
    std::vector<fifo> fifos;
    // 1.创建命名管道,进程
    if(!Createrfifo(&fifos))
        return 0;
    sleep(2);
    for(const auto& fifo : fifos)
        std::cout << fifo.FILENAME <<":" << fifo.workerid << ":" << fifo.ctrlfd << std::endl;

    // 2.发送任务
    sleep(2);
    bool g_always_loop = true;
    SendCommit(fifos, !g_always_loop, 10);

    // 3. 回收资源
    sleep(2);
    Releasefifo(fifos);
    return 0;
}

【ProcessPool.chh】

#pragma once 

#include <iostream>
#include <functional>
#include <vector>
#include <ctime>

using task_t = std::function<void()>;

void Download()
{
    std::cout << "我是一个下载任务" << std::endl;
}


void Printflog()
{
    std::cout << "我是一个打印日志任务" << std::endl;
}

void PushVideoStream()
{
    std::cout << "我是一个推送视频流任务" << std::endl;
}


class Init
{
public:
    //任务码
    static const int g_Download_code = 0;
    static const int g_Printflog_code = 1;
    static const int g_PushVideoStream_code = 2;
    std::vector<task_t> tasks;
public:
    Init()
    {
        tasks.push_back(Download);
        tasks.push_back(Printflog);
        tasks.push_back(PushVideoStream);
        srand(time(nullptr));
    }

    int SelectTask()
    {
        return rand() % tasks.size();
    }

    std::string CodeToName(int code)
    {
        switch(code)
        {
        case 0:
            return "Download";
        case 1:
            return "Printflog";
        case 2:
            return "PushVideoStream";
        default:
            return "Nothing";
        }
    }


    bool CheckSafe(int code)
    {
        return code >= 0 && code < tasks.size();
    }

    void RunTask(int code)
    {
        tasks[code]();
    }
};

Init init;

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

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

相关文章

数学建模·层次分析法

层次分析法 LAF 定义 具体用途 评价体系的优劣影响&#xff0c;计算评价指标的权重 主要步骤 关键在于一致性检验和求权值 权重的计算 注意权重之和为1&#xff0c;需要归一化 算数平均法 特征值法 矩阵的一致性检验 为什么要检验&#xff1f;&#xff1a;简单来说就…

Qt 实战(2)搭建开发环境 | 2.3、qmake详解

文章目录 一、qmake详解1、相关概念2、qmake作用3、运行qmake4、Qt Creator构建项目与执行qmake操作之间的区别4.1、功能与目的4.2、执行时机与流程 5、总结 前言&#xff1a; Qt qmake 是一个用于自动化生成 Makefile 的工具&#xff0c;它极大地简化了 Qt 应用程序和库的编译…

免费听书TV版v1.0.1

使用非常稳定流畅&#xff0c;UI界面设计美观简洁&#xff0c;纯净无广。资源虽然不是特别多&#xff0c;但是日常听书还是可以满足需求。 完全免费&#xff0c;操作简单方便&#xff0c;安装即用&#xff0c;没有任何限制。 可以适配遥控器操作&#xff0c;OK键开启或关闭语…

第二证券:销量暴跌95%,这一巨头市值蒸发超3000亿元!

在多重要素刺激下&#xff0c;PCB工作站上风口。 波音销量堕入停滞 6月仅售出3架客机 据央视财经&#xff0c;在一系列丑闻的影响下&#xff0c;波音公司本年出售遭到明显冲击。当地时间9日&#xff0c;波音发布的数据闪现&#xff0c;在以前一个月&#xff0c;该公司仅卖出…

【鸿蒙学习笔记】Stage模型

官方文档&#xff1a;Stage模型开发概述 目录标题 Stage模型好处Stage模型概念图ContextAbilityStageUIAbility组件和ExtensionAbility组件WindowStage Stage模型-组件模型Stage模型-进程模型Stage模型-ArkTS线程模型和任务模型关于任务模型&#xff0c;我们先来了解一下什么是…

旷野之间8 - LLMOps 与 MLOps操作化 AI 模型

介绍 随着人工智能越来越多地应用于商业应用&#xff0c;简化人工智能系统&#xff08;尤其是机器学习模型&#xff09;的开发和持续管理的新实践也不断涌现。MLOps 已成为一种基于 DevOps 原则实施机器学习的流行方法。 现在&#xff0c;随着 GPT-3 等大型语言模型 (LLM) 的…

火热夏季:浦语*书生InternLM大模型实战闯关-入门岛之Linux基础知识

一、ssh链接与端口映射并运行hello_wold.py 1.创建开发机 InternStudio创建开发机 2.进入开发机 3.Ssh链接开发机 powerShell终端ssh链接开发机。 4.创建一个hello_world.py文件web demo 5.运行web demo 6.端口映射 7.本地浏览器打开web 二、 VSCODE 远程连接开发机并创建一个…

LeetCode67(二进制求和[位运算,大数运算])

二进制求和 题目要求: 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 这道题其实有几种解法.我们先来介绍简单的方法. 我们可以将两个字符串的二进制转成十进制,获取对应值相加之后,我们可以不断对2取余,获取尾数拼接即可.也就是像我们平常求一…

笔试算法刷题

猿辅导2021校园招聘笔试&#xff08;算法一&#xff09; 牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推&#xff0c;求职就业一站解决_牛客网 (nowcoder.com) 第一眼看到这个题想到的是蓝桥杯飞机降落&#xff0c;贪心题。但是这样算的是最大不相交区间数量&#xff0…

docker笔记2

docker笔记2 一、阿里云镜像配置二、docker基本原理1.docker是如何启动一个容器的2.docker的底层原理 三、镜像命令总结 一、阿里云镜像配置 配置镜像的目的 由于Docker Hub等公共镜像仓库的服务器可能位于国外&#xff0c;直接从中拉取镜像时可能会遇到网络延迟或不稳定的问…

MySQL Undo Log

总结自bojiangzhou undo log称为撤销日志或回滚日志。在一个事务中进行增删改操作时&#xff0c;都会记录对应的 undo log。在对数据库进行修改前&#xff0c;会先记录对应的 undo log&#xff0c;然后在事务失败或回滚的时候&#xff0c;就可以用这些 undo log 来将数据回滚到…

(2024,测试时训练(TTT),线性注意力,RNN,嵌套循环)学习(在测试时学习):具有表达性隐藏状态的 RNN

Learning to (Learn at Test Time): RNNs with Expressive Hidden States 公和众与号&#xff1a;EDPJ&#xff08;进 Q 交流群&#xff1a;922230617 或加 VX&#xff1a;CV_EDPJ 进 V 交流群&#xff09; 目录 0. 摘要 1. 简介 2. 方法 2.1 使用 TTT 更新隐藏状态 2.2 …

常用的JVM启动参数

JVM的启动参数有很多&#xff0c;但是我们平常能用上的并不是特别多&#xff0c;这里介绍几个我们常用的&#xff1a; 1. 堆设置&#xff1a; 。 -Xms&#xff1a;设置堆的初始大小。 。.-Xmx&#xff1a;设置堆的最大大小。 2. 栈设置&#xff1a; 。 -XsS&#xff1a;设置每个…

​​​防御第一次作业

1、拓扑图及实验要求&#xff1a; 2、配置&#xff1a; 配置终端及服务器IP地址&#xff1a; Pc2&#xff1a; Client1&#xff1a; Pc4&#xff1a; Client2&#xff1a; PC1&#xff1a; Server1&#xff1a; Server2&#xff1a; 防火墙基础配置&#xff1a; [fw1]int g …

光学、SAR卫星影像助力洞庭湖决堤抢险(附带数据下载)

​​ 点击下方全系列课程学习 点击学习—>ArcGIS全系列实战视频教程——9个单一课程组合系列直播回放 点击学习——>遥感影像综合处理4大遥感软件ArcGISENVIErdaseCognition 7月5日下午&#xff0c;湖南岳阳市华容县团洲乡团北村团洲垸洞庭湖一线堤防发生决口&#xff0…

怎样在 PostgreSQL 中优化对 UUID 数据类型的索引和查询?

文章目录 一、UUID 数据类型概述二、UUID 索引和查询的性能问题三、优化方案&#xff08;一&#xff09;选择合适的索引类型&#xff08;二&#xff09;压缩 UUID&#xff08;三&#xff09;拆分 UUID&#xff08;四&#xff09;使用覆盖索引&#xff08;五&#xff09;优化查询…

Meta发布Llama 2驱动的AI代码生成器:Code Llama,开源来袭!

Meta 刚刚了号称是编程领域 “最先进的大语言模型”—— Code Llama &#xff0c;可根据 代码和自然语言提示 生成代码和有关代码的自然语言&#xff0c;支持多种主流编程语言&#xff0c; 包括 Python、C、Java、PHP、Typescript (Javascript)、C# 和 Bash 。 Code Llama 完全…

“Pandas数据处理与分析:实用技巧与应用“

目录 # 开篇 1. pandas的series的了解 1.1 pd.Series 创建 1.2 pd.series 的索引使用 1.3 pd.series 之字典/索引 1.4 pandas 转换数据类型 1.5 pandas 通过索引或者通过位置来取值 1.6 pandas 指定行取值 1.7 pands之Series 切片和索引 1.8 pands之Series 的索引和值…

vue2/3代码格式化问题,看着太难受了

1.原本的代码&#xff1a; 格式化后的代码&#xff1a; 太难受了&#xff01; 2.原本的代码 格式化后的代码 格式化跟有病似的&#xff0c;看着非常难受&#xff01; 有没有什么插件解决&#xff01;&#xff1f;

C++ //练习 14.44 编写一个简单的桌面计算器使其能处理二元运算。

C Primer&#xff08;第5版&#xff09; 练习 14.44 练习 14.44 编写一个简单的桌面计算器使其能处理二元运算。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代码块 /**********************************************************…