我像只鱼儿在你的进程池~

news2025/1/19 17:09:40

我像只鱼儿在你的进程池~

只为你守候这进程间通信~

一点都不押韵

点踩吧

父进程是master,它提前创建出一堆紫禁城,如果有命令就交给子进程去执行(和shell不一样,shell是有任务才去创建子进程),提前创建子进程,父子间需要进行通信,父进程持有写端,紫禁城具有读端,每个紫禁城都从管道中进行读取,这批提前创建出的紫禁城叫进程池,减小了创建进程的成本,管道里面没有数据,worker进程就阻塞等待数据的到来,master向哪一个管道写入,就是唤醒哪一个紫禁城来处理问题

专业团队:

 父进程肯定要雨露均沾,这个叫负载均衡

设计一下吧,进程池:

#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
#include<sys/types.h>

void work(int rfd)
{
    while(true)
    {
        sleep(1);
    }
}

//master
class Channel
{
public:
    Channel(int wfd, pid_t id, const std::string &name)
    :_wfd(wfd),_subprocessid(id),_name(name)
    {}
    int Getfd()
    {
        return _wfd;
    }
    pid_t GetProcessId()
    {
        return _subprocessid;
    }
    std::string GetName()
    {
        return _name;
    }
    ~Channel()
    {

    }
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name;
};

// ./processpool 5
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        std::cerr<<"Usage:"<<argv[0]<<"processnum"<<std::endl;
        return 1;
    }
    int num = std::stoi(argv[1]);

    std::vector<Channel> channels;
    for(int i = 0; i < num; i++)
    {
        //创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if(n < 0)
        {
            exit(1);
        }
        //创建紫禁城
        pid_t id = fork();
        if(id == 0)
        {
            //child
            close(pipefd[1]);
            work(pipefd[0]);
            close(pipefd[0]);
            exit(0);
        }
        //构建名字
        std::string channel_name = "Channel  " + std::to_string(i);
        //父进程
        close(pipefd[0]);
        channels.push_back(Channel(pipefd[1],id,channel_name));

        //close(pipefd[1]);
    }

    for(auto &channel : channels)
    {
        std::cout << " ---------------------- " <<std::endl;
        std::cout << channel.GetName() << std::endl;
        std::cout << channel.Getfd() << std::endl;
        std::cout << channel.GetProcessId() << std::endl;
    }
    sleep(100);
    return 0;
}

打印出来之后它输出都正常,符合预期:

 但是代码不太优雅,有点挫,所以改一改加点功能

在cpp中,const &是输出型参数,&是输入输出型参数,*也表示输出型参数

master向哪一个管道写入就是唤醒哪一个紫禁城来处理任务

是固定长度的四字节的数组下标,是对应的任务码,对应的是函数指针数组的下标

.hpp是C++中的一种头文件,允许声明和定义写在头一文件

声明和定义分离可以打包成库,但是如果没有需求就不用这样

比如写一个需要子进程执行的任务

#pragma once
#include<iostream>

#define TaskNum 3

typedef void(*task_t)();  //函数指针

task_t tasks[TaskNum];

void Print()
{
    std::cout<<"pineapple"<<std::endl;
}

void Download()
{
    std::cout<<"Download Task"<<std::endl;
}

void Flush()
{
    std::cout<<"Flush Task"<<std::endl;
}

void InitTask()
{
    tasks[0]= Print;
    tasks[1]=Download;
    tasks[2]=Flush;
}

void ExcuteTask(int number)
{
    if(number<0 || number>2)
    {
        return; 
    }
    tasks[number]();
}

综上写一段完整的,依次把任务交给子进程的代码

 

Processpool.cc:

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


void work(int rfd)
{
    while(true)
    {
        int command = 0;
        int n = read(rfd, &command,sizeof(command)); 
        if(n == sizeof(int))
        {
            std::cout << "pid is:" << getpid() << "handler task" << std::endl;
            ExcuteTask(command);
        }
        else if(n == 0)
        {
            std::cout<<"Pipe closed"<<std::endl;
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }
}

//master
class Channel
{
public:
    Channel(int wfd, pid_t id, const std::string &name)
    :_wfd(wfd),_subprocessid(id),_name(name)
    {}
    int Getfd()const
    {
        return _wfd;
    }
    pid_t GetProcessId()const
    {
        return _subprocessid;
    }
    std::string GetName()const
    {
        return _name;
    }
    ~Channel()
    {

    }
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name;
};

void CreateChannelAndSub(std::vector<Channel>* channels,int num1)
{
    for(int i = 0; i < num1; i++)
    {
        //创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if(n < 0)
        {
            perror("pipe");
            exit(1);
        }
        //创建紫禁城
        pid_t id = fork();
        if(id < 0)
        {
            perror("fork");
            exit(1);
        }
        if(id == 0)
        {
            //child
            close(pipefd[1]);
            work(pipefd[0]);
            close(pipefd[0]);
            exit(0);
        }
       
        //父进程
        close(pipefd[0]);
        //构建名字
        std::string channel_name = "Channel  " + std::to_string(i);
        channels->push_back(Channel(pipefd[1],id,channel_name));
        //close(pipefd[1]);
    }
}

int NextChannel(int channelnum)
{
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}

void SendTaskCommand(const Channel &channel,int taskcommand)
{
    size_t n = write(channel.Getfd(),&taskcommand,sizeof(taskcommand));
    if(n != sizeof(taskcommand))
    {
        perror("write");
    }
}

// ./processpool 5
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        std::cerr<<"Usage:"<<argv[0]<<"processnum"<<std::endl;
        return 1;
    }
    int num = std::stoi(argv[1]);
    LoadTask();

    std::vector<Channel> channels;
    //创建信道和子进程
    CreateChannelAndSub(&channels,num);

    //通过channel来控制紫禁城
    while(true)
    {
        sleep(1);
        //选择任务
        int taskcommand = Select();
        //选择信道和进程
        int channel_index = NextChannel(channels.size());
        //发送任务
        SendTaskCommand(channels[channel_index],taskcommand);

        std::cout << "taskcommand:" << taskcommand << "  channel:"\
        <<channels[channel_index].GetName() << "  sub process:"\
        << channels[channel_index].GetProcessId() << std::endl;
    }

    // for(auto &channel : channels)
    // {
    //     std::cout << " ---------------------- " <<std::endl;
    //     std::cout << channel.GetName() << std::endl;
    //     std::cout << channel.Getfd() << std::endl;
    //     std::cout << channel.GetProcessId() << std::endl;
    // }
    // sleep(100);
    return 0;
}

Task.hpp:

#pragma once
#include<iostream>
#include<ctime>
#include<cstdlib>
#include<sys/types.h>
#include<unistd.h>

#define TaskNum 3

typedef void(*task_t)();  //函数指针

task_t tasks[TaskNum];

void Print()
{
    std::cout<<"pineapple"<<std::endl;
}

void Download()
{
    std::cout<<"Download Task"<<std::endl;
}

void Flush()
{
    std::cout<<"Flush Task"<<std::endl;
}

void LoadTask()
{
    srand(time(nullptr) ^ getpid() ^ 114514);   //没别的意思我随便写的
    tasks[0]= Print;
    tasks[1]=Download;
    tasks[2]=Flush;
}

void ExcuteTask(int number)
{
    if(number<0 || number>2)
    {
        return; 
    }
    tasks[number]();
}

int Select()
{
    return rand() % TaskNum;
}

之前觉得chat很好用来着

但是这次进程池很明显它没理解我的意思,把我的类加了析构,还对这几个子进程进行了等待回收(回收是没问题的,但是它回收的时机不对,石矶的活!):

但是等会它好像也行?好像对但又好像不对

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

// Ensure proper function declarations
void LoadTask();
int Select();
void ExcuteTask(int command);

void work(int rfd)
{
    while (true)
    {
        int command = 0;
        ssize_t n = read(rfd, &command, sizeof(command)); 
        if (n == sizeof(int))
        {
            std::cout << "pid is: " << getpid() << " handling task" << std::endl;
            ExcuteTask(command);
        }
        else if (n == 0)
        {
            // Pipe closed; exit the loop if there's no more data
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }
}

class Channel
{
public:
    Channel(int wfd, pid_t id, const std::string &name)
    : _wfd(wfd), _subprocessid(id), _name(name)
    {}

    int Getfd() const
    {
        return _wfd;
    }

    pid_t GetProcessId() const
    {
        return _subprocessid;
    }

    std::string GetName() const
    {
        return _name;
    }

    ~Channel()
    {
        close(_wfd); // Close file descriptor in the destructor
    }

private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name;
};

void CreateChannelAndSub(std::vector<Channel>* channels, int num1)
{
    for (int i = 0; i < num1; i++)
    {
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if (n < 0)
        {
            perror("pipe");
            exit(1);
        }

        pid_t id = fork();
        if (id < 0)
        {
            perror("fork");
            exit(1);
        }
        if (id == 0)
        {
            close(pipefd[1]);
            work(pipefd[0]);
            close(pipefd[0]);
            exit(0);
        }

        close(pipefd[0]);
        std::string channel_name = "Channel " + std::to_string(i);
        channels->emplace_back(pipefd[1], id, channel_name);
    }
}

int NextChannel(int channelnum)
{
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}

void SendTaskCommand(const Channel &channel, int taskcommand)
{
    ssize_t n = write(channel.Getfd(), &taskcommand, sizeof(taskcommand));
    if (n != sizeof(taskcommand))
    {
        perror("write");
    }
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        std::cerr << "Usage: " << argv[0] << " processnum" << std::endl;
        return 1;
    }
    int num = std::stoi(argv[1]);
    LoadTask();

    std::vector<Channel> channels;
    CreateChannelAndSub(&channels, num);

    while (true)
    {
        int taskcommand = Select();
        int channel_index = NextChannel(channels.size());
        SendTaskCommand(channels[channel_index], taskcommand);

        std::cout << "taskcommand: " << taskcommand 
                  << " channel: " << channels[channel_index].GetName() 
                  << " sub process: " << channels[channel_index].GetProcessId() 
                  << std::endl;
    }

    // Wait for all child processes to terminate
    for (auto &channel : channels)
    {
        int status;
        waitpid(channel.GetProcessId(), &status, 0);
    }

    return 0;
}

 应该不对貌似是

不是好像是对的

是在析构之后关闭

关闭然后等待吗?蒙蒙的

子进程结束之后进行析构然后关闭对应的写文件描述符(不采用循环,一个结束关一个)

进行循环等待回收子进程

析构是关闭写的文件描述符

但是没有封装之后的代码优越

错大抵是没错的

我给他的本来也是一个半成品代码

都没通过channel对子进程实行控制

所以它写的照完备的应该还算正确,还是很好用的,只是需要自己注意下别踩坑才是

不对应该还是不对

没法正常关闭子进程好像是,等我试验下

它对的,我没看懂罢了啊啊啊啊

代码还是有点挫,需要加个函数控制下:

​//通过channel来控制紫禁城
void CtrlProcess(std::vector<Channel> &channels)
{
    while(true)
    {
        sleep(1);
        //选择任务
        int taskcommand = Select();
        //选择信道和进程
        int channel_index = NextChannel(channels.size());
        //发送任务
        SendTaskCommand(channels[channel_index],taskcommand);

        std::cout << "taskcommand:" << taskcommand << "  channel:"\
        <<channels[channel_index].GetName() << "  sub process:"\
        << channels[channel_index].GetProcessId() << std::endl;
    }
}

曝光每一个抽象群U:

想要回收管道和子进程怎么办捏?

首先需要关闭所有的写端,然后对子进程等待回收

完事了(有办法控制,回收正常):

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

void work(int rfd)
{
    while(true)
    {
        int command = 0;
        int n = read(rfd, &command,sizeof(command)); 
        if(n == sizeof(int))
        {
            std::cout << "pid is:" << getpid() << "handler task" << std::endl;
            ExcuteTask(command);
        }
        else if(n == 0)
        {
            std::cout<<"Pipe closed"<<std::endl;
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }
}

//master
class Channel
{
public:
    Channel(int wfd, pid_t id, const std::string &name)
    :_wfd(wfd),_subprocessid(id),_name(name)
    {}
    int Getfd()const
    {
        return _wfd;
    }
    pid_t GetProcessId()const
    {
        return _subprocessid;
    }
    std::string GetName()const
    {
        return _name;
    }
    void CloseChannel()
    {
        close(_wfd);
    }
    void Wait()
    {
        pid_t rid = waitpid(_subprocessid,nullptr,0);
        if(rid > 0)
        {
            std::cout << "wait " <<rid << "success" << std::endl;
        }
    }
    ~Channel()
    {

    }
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name;
};

void CreateChannelAndSub(std::vector<Channel>* channels,int num1)
{
    for(int i = 0; i < num1; i++)
    {
        //创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if(n < 0)
        {
            perror("pipe");
            exit(1);
        }
        //创建紫禁城
        pid_t id = fork();
        if(id < 0)
        {
            perror("fork");
            exit(1);
        }
        if(id == 0)
        {
            //child
            close(pipefd[1]);
            work(pipefd[0]);
            close(pipefd[0]);
            exit(0);
        }
       
        //父进程
        close(pipefd[0]);
        //构建名字
        std::string channel_name = "Channel  " + std::to_string(i);
        channels->push_back(Channel(pipefd[1],id,channel_name));
        //close(pipefd[1]);
    }
}

int NextChannel(int channelnum)
{
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}

void SendTaskCommand(const Channel &channel,int taskcommand)
{
    size_t n = write(channel.Getfd(),&taskcommand,sizeof(taskcommand));
    if(n != sizeof(taskcommand))
    {
        perror("write");
    }
}

void CtrlProcessOnce(std::vector<Channel> &channels)
{
    sleep(1);
    //选择任务
    int taskcommand = Select();
    //选择信道和进程
    int channel_index = NextChannel(channels.size());
    //发送任务
    SendTaskCommand(channels[channel_index],taskcommand);

    std::cout << "taskcommand:" << taskcommand << "  channel:"\
    <<channels[channel_index].GetName() << "  sub process:"\
    << channels[channel_index].GetProcessId() << std::endl;
}

//通过channel来控制紫禁城
void CtrlProcess(std::vector<Channel> &channels,int times = -1)
{
    if(times > 0)
    {
       while(times--)
        {
            CtrlProcessOnce(channels);
        }
    }
    else
    {
        while(true)
        {
            CtrlProcessOnce(channels);
        }
    }
}

//回收管道和子进程
void CleanUpChannels(std::vector<Channel> &channels)
{
    for(auto &channel : channels)
    {
        channel.CloseChannel();
    }

    for(auto &channel : channels)
    {
        channel.Wait();
    }
}

// ./processpool 5
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        std::cerr<<"Usage:"<<argv[0]<<"processnum"<<std::endl;
        return 1;
    }
    int num = std::stoi(argv[1]);
    LoadTask();

    std::vector<Channel> channels;
    //创建信道和子进程
    CreateChannelAndSub(&channels,num);

    //通过channel控制子进程
    CtrlProcess(channels,10);

    CleanUpChannels(channels);
   
    // for(auto &channel : channels)
    // {
    //     std::cout << " ---------------------- " <<std::endl;
    //     std::cout << channel.GetName() << std::endl;
    //     std::cout << channel.Getfd() << std::endl;
    //     std::cout << channel.GetProcessId() << std::endl;
    // }
    // sleep(100);
    return 0;
}

可以重定向一下让每次的读从标准输入读,而不是从管道中,这样做的好处是紫禁城执行这些再也不用操心勒!(不关心是否从管道里读)

创建出的紫禁城要帮助我们执行任务,而work本身也是一项任务,task_t task被称为回调函数,回调work函数,而work函数被存放在Task.hpp中,这样可以进行解耦(将耦合度降低)

改完的大概是这样:

ProcessPool.cc:

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

// void work(int rfd)
// {
//     while(true)
//     {
//         int command = 0;
//         int n = read(rfd, &command,sizeof(command)); 
//         if(n == sizeof(int))
//         {
//             std::cout << "pid is:" << getpid() << "handler task" << std::endl;
//             ExcuteTask(command);
//         }
//         else if(n == 0)
//         {
//             std::cout<<"Pipe closed"<<std::endl;
//             break;
//         }
//         else
//         {
//             perror("read");
//             break;
//         }
//     }
// }


//master

class Channel
{
public:
    Channel(int wfd, pid_t id, const std::string &name)
    :_wfd(wfd),_subprocessid(id),_name(name)
    {}
    int Getfd()const
    {
        return _wfd;
    }
    pid_t GetProcessId()const
    {
        return _subprocessid;
    }
    std::string GetName()const
    {
        return _name;
    }
    void CloseChannel()
    {
        close(_wfd);
    }
    void Wait()
    {
        pid_t rid = waitpid(_subprocessid,nullptr,0);
        if(rid > 0)
        {
            std::cout << "wait " <<rid << "success" << std::endl;
        }
    }
    ~Channel()
    {

    }
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name;
};

void CreateChannelAndSub(std::vector<Channel>* channels,int num1,task_t task)
{
    for(int i = 0; i < num1; i++)
    {
        //创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if(n < 0)
        {
            perror("pipe");
            exit(1);
        }
        //创建紫禁城
        pid_t id = fork();
        if(id < 0)
        {
            perror("fork");
            exit(1);
        }
        if(id == 0)
        {
            //child
            close(pipefd[1]);
            dup2(pipefd[0],0);
            task();
            close(pipefd[0]);
            exit(0);
        }
       
        //父进程
        close(pipefd[0]);
        //构建名字
        std::string channel_name = "Channel  " + std::to_string(i);
        channels->push_back(Channel(pipefd[1],id,channel_name));
        //close(pipefd[1]);
    }
}

int NextChannel(int channelnum)
{
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}

void SendTaskCommand(const Channel &channel,int taskcommand)
{
    size_t n = write(channel.Getfd(),&taskcommand,sizeof(taskcommand));
    if(n != sizeof(taskcommand))
    {
        perror("write");
    }
}

void CtrlProcessOnce(std::vector<Channel> &channels)
{
    sleep(1);
    //选择任务
    int taskcommand = Select();
    //选择信道和进程
    int channel_index = NextChannel(channels.size());
    //发送任务
    SendTaskCommand(channels[channel_index],taskcommand);

    std::cout << "taskcommand:" << taskcommand << "  channel:"\
    <<channels[channel_index].GetName() << "  sub process:"\
    << channels[channel_index].GetProcessId() << std::endl;
}

//通过channel来控制紫禁城
void CtrlProcess(std::vector<Channel> &channels,int times = -1)
{
    if(times > 0)
    {
       while(times--)
        {
            CtrlProcessOnce(channels);
        }
    }
    else
    {
        while(true)
        {
            CtrlProcessOnce(channels);
        }
    }
}

//回收管道和子进程
void CleanUpChannels(std::vector<Channel> &channels)
{
    for(auto &channel : channels)
    {
        channel.CloseChannel();
    }

    for(auto &channel : channels)
    {
        channel.Wait();
    }
}

// ./processpool 5
int main(int argc,char* argv[])
{
    if(argc!=2)
    {
        std::cerr<<"Usage:"<<argv[0]<<"processnum"<<std::endl;
        return 1;
    }
    int num = std::stoi(argv[1]);
    LoadTask();

    std::vector<Channel> channels;
    //创建信道和子进程
    CreateChannelAndSub(&channels,num,work);

    //通过channel控制子进程
    CtrlProcess(channels,10);

    CleanUpChannels(channels);
   
    // for(auto &channel : channels)
    // {
    //     std::cout << " ---------------------- " <<std::endl;
    //     std::cout << channel.GetName() << std::endl;
    //     std::cout << channel.Getfd() << std::endl;
    //     std::cout << channel.GetProcessId() << std::endl;
    // }
    // sleep(100);
    return 0;
}

Task.hpp:

#pragma once
#include<iostream>
#include<ctime>
#include<cstdlib>
#include<sys/types.h>
#include<unistd.h>

#define TaskNum 3

typedef void(*task_t)();  //函数指针

task_t tasks[TaskNum];

void work()
{
    while(true)
    {
        int command = 0;
        int n = read(0, &command,sizeof(command)); 
        if(n == sizeof(int))
        {
            std::cout << "pid is:" << getpid() << "handler task" << std::endl;
            ExcuteTask(command);
        }
        else if(n == 0)
        {
            std::cout<<"Pipe closed"<<std::endl;
            break;
        }
        else
        {
            perror("read");
            break;
        }
    }
}

void Print()
{
    std::cout<<"pineapple"<<std::endl;
}

void Download()
{
    std::cout<<"Download Task"<<std::endl;
}

void Flush()
{
    std::cout<<"Flush Task"<<std::endl;
}

void LoadTask()
{
    srand(time(nullptr) ^ getpid() ^ 114514);   //没别的意思我随便写的
    tasks[0]= Print;
    tasks[1]=Download;
    tasks[2]=Flush;
}

void ExcuteTask(int number)
{
    if(number<0 || number>2)
    {
        return; 
    }
    tasks[number]();
}

int Select()
{
    return rand() % TaskNum;
}

但是有个问题:回收的代码:

void CleanUpChannels(std::vector<Channel> &channels)
{
    for(auto &channel : channels)
    {
        channel.CloseChannel();
    }

    for(auto &channel : channels)
    {
        channel.Wait();
    }
}

 能不能这样改:

void CleanUpChannels(std::vector<Channel> &channels)
{
    for(auto &channel : channels)
    {
        channel.CloseChannel();
        channel.Wait();
    }
}

试试不就知道了

结果就是阻塞在那

也不退出

为什么捏?

因为我们的代码默认有bug

父进程有的文件描述符表是3456...

一旦创建了管道,紫禁城会继承父进程的文件描述符表

所以一个非常大的问题是,管道的写端可能不止一个文件描述符指向

但是我们关闭的时候只关闭了父进程的一个

紫禁城还有很多货接着指向呢

引用计数不为0,那文件就不会被销毁,所以接着阻塞,即读端什么都读不到

但是为什么上面那样两个循环就可以了呢?

因为那样是从上向下关闭,递归式

也就是说这样也是可以的:

void CleanUpChannel(std::vector<Channel> &channels)
{
    int num = channels.size() - 1;
    while(num >= 0)
    {
        channels[num].CloseChannel();
        channels[num--].Wait();
    }
}

但是做人不能这样

你明知道有bug还不修改么?

但是改bug的事下次再说罢

我忙着看案子呢拜拜

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

吹爆SyntaxFlow!数据流分析实战解析

正文开始前辟个谣先 最近有小伙伴来问闭源收费的事 牛牛郑重告知大家 目前还没有这个计划 请大家放心使用 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 样例解析在之前的SyntaxFlow教程中&#xff0c;我们已经看到了非常多的代码样例进行数据流分…

广东失业领取指南 记录自己的失业

1. 前言 坐标广州&#xff0c;七月底就被裁员领取n1解放了&#xff0c;趁着这次机会&#xff0c;也想着躺几个月后再去上班&#xff0c;现在工作也不好找&#xff0c;加上之前加班实在是消耗太大了。想着躺也不能干躺&#xff0c;去申请一下失业金每个月还有补贴用&#xff0c…

[数据集][目标检测]锤子检测数据集VOC+YOLO格式1510张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1510 标注数量(xml文件个数)&#xff1a;1510 标注数量(txt文件个数)&#xff1a;1510 标注…

悟空降世 撼动全球

文&#xff5c;琥珀食酒社 作者 | 积溪 一只猴子能值多少钱&#xff1f; 答案是&#xff1a;13个小目标 这两天 只要你家没有断网 一定会被这只猴子刷屏 它就是咱国产的3A游戏 《黑神话&#xff1a;悟空》 这只猴子到底有多火&#xff1f; 这么跟你说吧 茅台见了它都…

TPM管理培训值不值得去?

TPM管理培训是否值得去&#xff0c;取决于多个因素&#xff0c;包括个人职业发展需求、所在企业的情况以及培训内容和质量等&#xff0c;具体如深圳TPM管理咨询公司下文所述&#xff1a; 一、个人职业发展需求 - 从事设备管理相关工作&#xff1a;如果您的工作涉及设备维护、生…

数聚股份携手销售易闪耀CDIE数字化创新博览会,开启企业数字化转型加速器

6月25日-26日&#xff0c;第十届CDIE数字化创新博览会在上海张江科学会堂隆重举办&#xff0c;本次博览会大咖云集&#xff0c;汇聚零售、快消、制造、医疗、金融、出海等领域的企业高管和部门负责人&#xff0c;打破行业边界、共话数字化痛点和热点话题。 时隔一年&#xff…

UDS 诊断 - ReadMemoryByAddress(按地址读取内存)(0x23)服务

UDS 诊断服务系列文章目录 诊断和通信管理功能单元 UDS 诊断 - DiagnosticSessionControl&#xff08;诊断会话控制&#xff09;&#xff08;0x10&#xff09;服务 UDS 诊断 - ECUReset&#xff08;ECU重置&#xff09;&#xff08;0x11&#xff09;服务 UDS 诊断 - SecurityA…

黑神话孙悟空,自媒体小白如何蹭这波流量!

当下&#xff0c;黑神话孙悟空的热度如熊熊烈火&#xff0c;席卷了整个游戏世界。 只要与这个话题沾边&#xff0c;似乎就能轻松吸引大量关注。 那么&#xff0c;对于不怎么懂自媒体运营的小伙伴来说&#xff0c;该如何抓住这个机遇呢&#xff1f; 别担心&#xff0c;我们可以…

LearnOpenGL——延迟渲染学习笔记

延迟渲染学习笔记 一、基本概念二、G-BufferMRT 三、Lighting Pass四、结合延迟渲染和前向渲染五、更多光源 我们之前使用的一直是 前向渲染&#xff08;正向渲染 Forward Rendering&#xff09;&#xff0c;指的是在场景中根据所有光源照亮一个物体&#xff0c;之后再渲染下一…

MBR30100CT-ASEMI低压降肖特基MBR30100CT

编辑&#xff1a;ll MBR30100CT-ASEMI低压降肖特基MBR30100CT 型号&#xff1a;MBR30100CT 品牌&#xff1a;ASEMI 封装&#xff1a;TO-220 批号&#xff1a;最新 恢复时间&#xff1a;35ns 最大平均正向电流&#xff08;IF&#xff09;&#xff1a;30A 最大循环峰值反…

Django | 从中间件的角度来认识Django发送邮件功能

文章目录 概要中间件中间件 ---> 钩子实现中间件demo 邮件发送过程Django如何做邮件服务配置流程 中间件结合邮件服务实现告警 概要 摘要 业务告警 邮件验证 密码找回 邮件告警 中间件 中间件 —> ‘钩子’ 例如 访问路由 的次数【请求】 中间件类须实现下列五个方法…

商业模式画布全面解读:产品经理的实用手册

在产品经理的日常工作中&#xff0c;最基本的一项任务就是进行竞品分析和商业模式分析。一方面&#xff0c;通过竞品分析环节&#xff0c;了解当前产品的竞争力和不可替代性&#xff1b;另一方面&#xff0c;定期评估产品的商业模式是否存在风险&#xff0c;是否具有可持续性。…

Destiny of Gods首轮测试正式开启,参与玩家数量突破10万

天神风云&#xff0c;波澜再兴&#xff0c;GameFi链游聚合平台Destiny of Gods首款同名数字卡牌回合制游戏首轮测试定档8月20日20:00&#xff08;GMT8&#xff09;&#xff0c;现已正式开启&#xff01; 这是一个由人、游灵和神灵共存的世界&#xff0c;历经蛮荒时期的纷争与信…

Docker的安装与镜像配置

小编目前大一&#xff0c;刚开始着手学习SSM&#xff0c;小编会把每个框架都整理成知识点发布出来。如果你也同时在学习SSM的话&#xff0c;不妨看看我做的这个笔记。我认为同为初学者&#xff0c;我把我对知识点的理解以这种代码加观点的方式分享出来不仅加深了我的理解&#…

游泳耳机哪个牌子好?角逐必选榜的4大王者游泳耳机测评解析!

在选择游泳耳机时&#xff0c;许多消费者往往会被市场上五花八门的产品所困扰。特别是那些标榜能够防水防潮的产品&#xff0c;但实际上它们往往缺乏核心技术支持&#xff0c;存在很高的损伤风险。据调查&#xff0c;超过90%的用户反映&#xff0c;市面上的游泳耳机常常无法达到…

centos7.9离线安装kubernetes(k8s)1.28版本

文章目录 一、环境准备二、所有节点执行1、解压安装包2、创建/etc/modules-load.d/containerd.conf配置文件3、执行以下命令使配置生效4、创建/etc/sysctl.d/99-kubernetes-cri.conf配置文件5、加载ipvs内核模块–4.19以上版本内核6、授权生效7、关闭swap&#xff0c;并永久关闭…

WPF——动态排名图表实现

开发环境 VS2022 .NET 8.0 MVVM Toolkit 8.2.2 需求 开发中需要实现按照成绩动态指名&#xff0c;以展示当前的竞赛成绩的一个实时情况及变化。 即如下效果&#xff1a; 需求分析 按照接收到的信息&#xff0c;就是要将获取到的集合排序&#xff0c;并且要将排序前后的变…

什么是UDP?

UDP是工作在OSI&#xff08;开放系统互连&#xff0c;Open Systems Interconnection&#xff09;模型中传输层的协议。它使用IP作为底层协议&#xff0c;是为应用程序提供一种以最少的协议机制向其他程序发送消息的协议。其主要特点是无连接&#xff0c;不保证可靠传输和面向报…

汽车线束智能制造:MES系统与工艺深度融合的革新之路

万界星空科技汽车线束工厂MES系统解决方案是针对线束制造行业特定需求而设计的一套集成化管理系统&#xff0c;旨在提高生产效率、优化资源配置、确保产品质量并增强企业竞争力。 一、汽车线束制造工艺 汽车线束&#xff0c;作为连接汽车各个电子部件的桥梁&#xff0c;承载着…

论文:NeRF on the go:Exploiting Uncertainty for Distractor-free NeRFs in the Wild

随时随地使用NeRF。本文的目标是利用在野外随意捕捉的图像序列或视频作为输入&#xff0c;训练一个用于静态场景的NeRF&#xff0c;并有效地移除场景中的所有动态元素&#xff08;如汽车、电车、行人等&#xff09;&#xff0c;即干扰物。与现有的方法如NeRF-W [27]和RobustNeR…