进程间通信-进程池

news2025/1/8 20:34:09

目录

理解​

完整代码

 完善代码

 回收子进程:​

 不回收子进程:

子进程使用重定向优化


理解


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

void work(int rfd)
{
}

// master
class Channel
{
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name; // 信道名字
public:
    Channel(int wfd, pid_t id, const std::string name) // 构造函数
        : _wfd(wfd), _subprocessid(id), _name(name)    // 构造函数初始化列表
    {
    }

    int getfd() { return _wfd; }
    int getid() { return _subprocessid; }
    std::string getname() { return _name; }

    ~Channel() // 析构函数
    {
    }
};

//  ./processpool 5
int main(int argc, char *argv[])
{
    std::vector<Channel> channels;
    if (argc != 2) // 说明不用创建子进程,不会用
    {
        std::cerr << "usage: " << argv[0] << "processnum" << std::endl;
        return 1;
    }
    int num = std::stoi(argv[1]); // 表明需要创建几个子进程,stoi转化整数
    for(int i=0;i<num;i++)
    {
        int pipefd[2];
        int n=pipe(pipefd);//创建管道
        if(n<0)exit(1);//创建管道失败,那么就无需与子进程通信了

        pid_t id=fork();//创建子进程
        if(id==0){//子进程不用创建子进程,只需父进程即可
            //child  --r
            close(pipefd[1]);
            work(pipefd[0]);//进行工作
            exit(0);
        }

        //farther  --w
        close(pipefd[0]);
        //a.此时父进程已经有了子进程的pid  b.父进程的w端 
        std::string channel_name="channel-"+std::to_string(i);//构建一个channel名称
        channels.push_back(Channel(pipefd[1],id,channel_name));
    }
    // test
    for (auto &channel : channels)
    {
        std::cout << "***************************************" << std::endl;
        std::cout << channel.getname() << std::endl; // 取出
        std::cout << channel.getid() << std::endl;   // 取出
        std::cout << channel.getfd() << std::endl;   // 取出
    }

    return 0;
}

完整代码

#include <iostream>
#include <unistd.h>
#include <string>
#include <vector>
#include <sys/types.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()<<"chuli task"<<std::endl;
            excuttask(command); // 执行
        }
    }
}

// master
class Channel
{
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name; // 信道名字
public:
    Channel(int wfd, pid_t id, const std::string name) // 构造函数
        : _wfd(wfd), _subprocessid(id), _name(name)    // 构造函数初始化列表
    {
    }
    int getfd() { return _wfd; }
    int getid() { return _subprocessid; }
    std::string getname() { return _name; }
    ~Channel() // 析构函数
    {
    }
};

// 形参类型和命名规范
//  const & :输出
//  & :输入输出型参数
//  * :输出型参数
void creatchannelandsun(int num, std::vector<Channel> *channels)
{
    for (int i = 0; i < num; i++)
    {
        int pipefd[2];
        int n = pipe(pipefd); // 创建管道
        if (n < 0)
            exit(1); // 创建管道失败,那么就无需与子进程通信了

        pid_t id = fork(); // 创建子进程
        if (id == 0)
        { // 子进程不用创建子进程,只需父进程即可
            // child  --r
            close(pipefd[1]);
            work(pipefd[0]); // 进行工作
            exit(0);
        }

        // farther  --w
        close(pipefd[0]);
        // a.此时父进程已经有了子进程的pid  b.父进程的w端
        std::string channel_name = "channel-" + std::to_string(i); // 构建一个channel名称
        channels->push_back(Channel(pipefd[1], id, channel_name));
    }
}

int nextchannel(int channelnum)
{ // 形成一个0 1 ...到channelnum的编号
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}

void sendtask(Channel &channel, int taskcommand)
{
    write(channel.getfd(), &taskcommand, sizeof(taskcommand)); // 写
}

//  ./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]); // 表明需要创建几个子进程,stoi转化整数
    inittask();                   // 装载任务

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

    // 通过channel控制子进程
    while (true)
    {
        sleep(1);//每隔一秒发布一个
        // 第一步选择一个任务 
        int taskcommand = selecttask();
        // 第二步选择一个信道和进程,其实是在vector中选择
        int index_channel = nextchannel(channels.size());
        // 第三步发送任务
        sendtask(channels[index_channel], taskcommand);
        std::cout<<std::endl;
std::cout<<"taskcommand: "<<taskcommand<<" channel: "<<channels[index_channel].getname()<<" subprocess: "<<channels[index_channel].getid()<<std::endl;
    }
    return 0;
}
//以前我们都是用.h表示头文件声明,.cpp表示实现
//那么我们用.hpp也是c++的一种头文件,他允许将声明和实现和在一个文件里,那么就有一个好处,像这种代码无法形成库,即使形成库也是开源形成的
#pragma once
#include<iostream>
#include<ctime>
#include<stdlib.h>
#include<unistd.h>
#include <sys/types.h>

#define tasknum 3

typedef void (*task_t)();//task_t  返回值为void,参数为空的函数指针

void print(){//三个任务列表
    std::cout<<"i am a printf task"<<std::endl;
}
void download(){
    std::cout<<"i am a download task"<<std::endl;
}
void flush(){
    std::cout<<"i am a flush task"<<std::endl;
}

task_t tasks[tasknum];//函数指针数组

void inittask(){//初始化任务
    srand(time(nullptr)^getpid());//种时间和pid随机种子
    //srand(seed): 这个函数用于用指定的 seed(种子)初始化随机数生成器。
    //seed 的值决定了 rand() 生成的随机数序列。如果使用相同的种子值,每次生成的随机数序列都是一样的。
    //time(nullptr) 提供了一个基于当前时间的种子值。getpid() 提供了一个进程的唯一标识符。
    //用异或操作符 ^ 将这两个值混合在一起,产生一个更为变化的种子值。
    tasks[0]=print;
    tasks[1]=download;
    tasks[2]=flush;
}

void excuttask(int n){//执行任务
    if(n<0||n>2)return;
    tasks[n]();//调用
}

int selecttask(){//随机选择任务
    return rand()%tasknum;
}

 完善代码

#include <iostream>
#include <unistd.h>
#include <string>
#include <vector>
#include<sys/wait.h>
#include <sys/types.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()<<"chuli task"<<std::endl;
            excuttask(command); // 执行
        }
        else if(n==0){
            std::cout<<"sub process: "<<getpid()<<" quit"<<std::endl;
            break;
        }
    }
}

// master
class Channel
{
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name; // 信道名字
public:
    Channel(int wfd, pid_t id, const std::string name) // 构造函数
        : _wfd(wfd), _subprocessid(id), _name(name)    // 构造函数初始化列表
    {
    }
    int getfd() { return _wfd; }
    int getid() { return _subprocessid; }
    std::string getname() { return _name; }
    void closechannel(){//关闭文件描述符
        close(_wfd);
    }
    void wait(){
        pid_t n=waitpid(_subprocessid,nullptr,0);
        if(n>0){
            std::cout<<"wait "<<n<<" success"<<std::endl;
        }
    }
    ~Channel() // 析构函数
    {
    }
};

// 形参类型和命名规范
//  const & :输出
//  & :输入输出型参数
//  * :输出型参数
void creatchannelandsun(int num, std::vector<Channel> *channels)
{
    for (int i = 0; i < num; i++)
    {
        int pipefd[2];
        int n = pipe(pipefd); // 创建管道
        if (n < 0)
            exit(1); // 创建管道失败,那么就无需与子进程通信了

        pid_t id = fork(); // 创建子进程
        if (id == 0)
        { // 子进程不用创建子进程,只需父进程即可
            // child  --r
            close(pipefd[1]);
            work(pipefd[0]); // 进行工作
            exit(0);
        }

        // farther  --w
        close(pipefd[0]);
        // a.此时父进程已经有了子进程的pid  b.父进程的w端
        std::string channel_name = "channel-" + std::to_string(i); // 构建一个channel名称
        channels->push_back(Channel(pipefd[1], id, channel_name));
    }
}

int nextchannel(int channelnum)
{ // 形成一个0 1 ...到channelnum的编号
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}

void sendtask(Channel &channel, int taskcommand)
{
    write(channel.getfd(), &taskcommand, sizeof(taskcommand)); // 写
}

void ctrlprocessonce(std::vector<Channel> &channels){//只做一次任务
        sleep(1);//每隔一秒发布一个
        // 第一步选择一个任务 
        int taskcommand = selecttask();
        // 第二步选择一个信道和进程,其实是在vector中选择
        int index_channel = nextchannel(channels.size());
        // 第三步发送任务
        sendtask(channels[index_channel], taskcommand);
        std::cout<<std::endl;
std::cout<<"taskcommand: "<<taskcommand<<" channel: "<<channels[index_channel].getname()<<" subprocess: "<<channels[index_channel].getid()<<std::endl;
}

void ctrlprocess(std::vector<Channel> &channels,int times=-1){
    if(times>0){//固定次数
        while(times--){//根据times控制
            ctrlprocessonce(channels);
        }
    }
    else{//缺省一直
    while(true){//一直控制
        ctrlprocessonce(channels);
    }
    }
}

//  ./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]); // 表明需要创建几个子进程,stoi转化整数
    inittask();                   // 装载任务

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

    // 通过channel控制子进程
    ctrlprocess(channels,10);//控制10次退出

    //回收进程,把写端关闭那么所有子进程读到0就break退出了
    for(auto &channel:channels){
        channel.closechannel();
    }

    //注意进程回收,则遍历关闭
    for(auto &channel:channels){
        channel.wait();
    }//如果不等待那么子进程就是僵尸进程了
    return 0;
}
//以前我们都是用.h表示头文件声明,.cpp表示实现
//那么我们用.hpp也是c++的一种头文件,他允许将声明和实现和在一个文件里,那么就有一个好处,像这种代码无法形成库,即使形成库也是开源形成的
#pragma once
#include<iostream>
#include<ctime>
#include<stdlib.h>
#include<unistd.h>
#include <sys/types.h>

#define tasknum 3

typedef void (*task_t)();//task_t  返回值为void,参数为空的函数指针

void print(){//三个任务列表
    std::cout<<"i am a printf task"<<std::endl;
}
void download(){
    std::cout<<"i am a download task"<<std::endl;
}
void flush(){
    std::cout<<"i am a flush task"<<std::endl;
}

task_t tasks[tasknum];//函数指针数组

void inittask(){//初始化任务
    srand(time(nullptr)^getpid());//种时间和pid随机种子
    //srand(seed): 这个函数用于用指定的 seed(种子)初始化随机数生成器。
    //seed 的值决定了 rand() 生成的随机数序列。如果使用相同的种子值,每次生成的随机数序列都是一样的。
    //time(nullptr) 提供了一个基于当前时间的种子值。getpid() 提供了一个进程的唯一标识符。
    //用异或操作符 ^ 将这两个值混合在一起,产生一个更为变化的种子值。
    tasks[0]=print;
    tasks[1]=download;
    tasks[2]=flush;
}

void excuttask(int n){//执行任务
    if(n<0||n>2)return;
    tasks[n]();//调用
}

int selecttask(){//随机选择任务
    return rand()%tasknum;
}

 回收子进程:

 不回收子进程:

子进程使用重定向优化

#include <iostream>
#include <unistd.h>
#include <string>
#include <vector>
#include<sys/wait.h>
#include <sys/types.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()<<"chuli task"<<std::endl;
//             excuttask(command); // 执行
//         }
//         else if(n==0){
//             std::cout<<"sub process: "<<getpid()<<" quit"<<std::endl;
//             break;
//         }
//     }
// }

void work()//执行任务工作
{
    while (true)
    {
        int command = 0;
        int n = read(0, &command, sizeof(command)); // 父进程写了一个整数,那么我也读一个整数
        //此时从标准输入去读,没有管道的概念了,对于子进程来说有人通过标准输入将任务给你
        if (n == sizeof(int))
        {
            std::cout<<"pid is "<<getpid()<<"chuli task"<<std::endl;
            excuttask(command); // 执行
        }
        else if(n==0){
            std::cout<<"sub process: "<<getpid()<<" quit"<<std::endl;
            break;
        }
    }
}

// master
class Channel
{
private:
    int _wfd;
    pid_t _subprocessid;
    std::string _name; // 信道名字
public:
    Channel(int wfd, pid_t id, const std::string name) // 构造函数
        : _wfd(wfd), _subprocessid(id), _name(name)    // 构造函数初始化列表
    {
    }
    int getfd() { return _wfd; }
    int getid() { return _subprocessid; }
    std::string getname() { return _name; }
    void closechannel(){//关闭文件描述符
        close(_wfd);
    }
    void wait(){
        pid_t n=waitpid(_subprocessid,nullptr,0);
        if(n>0){
            std::cout<<"wait "<<n<<" success"<<std::endl;
        }
    }
    ~Channel() // 析构函数
    {
    }
};

// 形参类型和命名规范
//  const & :输出
//  & :输入输出型参数
//  * :输出型参数
void creatchannelandsun(int num, std::vector<Channel> *channels)
{
    for (int i = 0; i < num; i++)
    {
        int pipefd[2];
        int n = pipe(pipefd); // 创建管道
        if (n < 0)
            exit(1); // 创建管道失败,那么就无需与子进程通信了

        pid_t id = fork(); // 创建子进程
        if (id == 0)
        { // 子进程不用创建子进程,只需父进程即可
            // child  --r
            close(pipefd[1]);
            dup2(pipefd[0],0);//将管道的读端,重定向到标准输入
            //本来应该在管道的读端读任务,现在做重定向,把0号文件描述符指向管道的读端
            work(); // 进行工作,此时不用传参
            exit(0);
        }

        // farther  --w
        close(pipefd[0]);
        // a.此时父进程已经有了子进程的pid  b.父进程的w端
        std::string channel_name = "channel-" + std::to_string(i); // 构建一个channel名称
        channels->push_back(Channel(pipefd[1], id, channel_name));
    }
}

int nextchannel(int channelnum)//选定一个管道
{ // 形成一个0 1 ...到channelnum的编号
    static int next = 0;
    int channel = next;
    next++;
    next %= channelnum;
    return channel;
}

void sendtask(Channel &channel, int taskcommand)//派发什么任务
{
    write(channel.getfd(), &taskcommand, sizeof(taskcommand)); // 写
}

void ctrlprocessonce(std::vector<Channel> &channels){//只做一次任务
        sleep(1);//每隔一秒发布一个
        // 第一步选择一个任务 
        int taskcommand = selecttask();
        // 第二步选择一个信道和进程,其实是在vector中选择
        int index_channel = nextchannel(channels.size());
        // 第三步发送任务
        sendtask(channels[index_channel], taskcommand);
        std::cout<<std::endl;
std::cout<<"taskcommand: "<<taskcommand<<" channel: "<<channels[index_channel].getname()<<" subprocess: "<<channels[index_channel].getid()<<std::endl;
}

void ctrlprocess(std::vector<Channel> &channels,int times=-1){//控制派发任务次数
    if(times>0){//固定次数
        while(times--){//根据times控制
            ctrlprocessonce(channels);
        }
    }
    else{//缺省一直
    while(true){//一直控制
        ctrlprocessonce(channels);
    }
    }
}

//  ./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]); // 表明需要创建几个子进程,stoi转化整数
    inittask();                   // 装载任务

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

    // 通过channel控制子进程
    ctrlprocess(channels,10);//控制10次退出
    //或者ctrlprocess(channels);//使用缺省参数一直控制

    //回收进程,把写端关闭那么所有子进程读到0就break退出了
    for(auto &channel:channels){
        channel.closechannel();
    }
    //注意进程回收,则遍历关闭
    for(auto &channel:channels){
        channel.wait();
    }//如果不等待那么子进程就是僵尸进程了
    return 0;
}
//以前我们都是用.h表示头文件声明,.cpp表示实现
//那么我们用.hpp也是c++的一种头文件,他允许将声明和实现和在一个文件里,那么就有一个好处,像这种代码无法形成库,即使形成库也是开源形成的
#pragma once
#include<iostream>
#include<ctime>
#include<stdlib.h>
#include<unistd.h>
#include <sys/types.h>

#define tasknum 3

typedef void (*task_t)();//task_t  返回值为void,参数为空的函数指针

void print(){//三个任务列表
    std::cout<<"i am a printf task"<<std::endl;
}
void download(){
    std::cout<<"i am a download task"<<std::endl;
}
void flush(){
    std::cout<<"i am a flush task"<<std::endl;
}

task_t tasks[tasknum];//函数指针数组

void inittask(){//初始化任务
    srand(time(nullptr)^getpid());//种时间和pid随机种子
    //srand(seed): 这个函数用于用指定的 seed(种子)初始化随机数生成器。
    //seed 的值决定了 rand() 生成的随机数序列。如果使用相同的种子值,每次生成的随机数序列都是一样的。
    //time(nullptr) 提供了一个基于当前时间的种子值。getpid() 提供了一个进程的唯一标识符。
    //用异或操作符 ^ 将这两个值混合在一起,产生一个更为变化的种子值。
    tasks[0]=print;
    tasks[1]=download;
    tasks[2]=flush;
}

void excuttask(int n){//执行任务
    if(n<0||n>2)return;
    tasks[n]();//调用
}

int selecttask(){//随机选择任务
    return rand()%tasknum;
}

 那么有个问题?为什么不能关闭一个再等待

 为什么呢?这是一个bug

 为什么全部关闭后再挨个等待就可以呢?

假如有10个子进程,那么第一个子进程有一个读端10个写端,第二个子进程9个写端1个读端,最后一个进程1个读端1个写端;
如果我们把它全部关完了,那么此时他从上往下遍历,管道最后只有最后一个会释放,那么它对应的上一个管道的写端也会释放,所以递归式的逆向关闭;

需要注意的是函数名就是地址;

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

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

相关文章

Windows下使用cmake编译OpenCV

Windows下使用cmake编译OpenCV cmake下载OpenCV下载编译OpenCV cmake下载 下载地址&#xff1a;https://cmake.org/download/ 下载完成&#xff0c;点击选择路径安装即可 OpenCV下载 下载地址&#xff1a;https://github.com/opencv/opencv/releases/tag/4.8.1因为我们是编译…

2024软件测试需要具备的技能(软技能硬技能)

软件测试的必备技能 在往期的文章分享了很多的面试题&#xff0c;索性做一个转型。从零基础开始讲解&#xff0c;结合面试题来和大家一起学习交流软件测试的艺术。 第一个是专业技能&#xff0c;也叫硬技能。 第二个叫做软技能。 我们在上一篇文章中讲到了软件测试流程的5个…

ChatGPT在论文写作领域的应用:初稿设计

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 学术论文写作中&#xff0c;内容清晰、结构合理的初稿至关重要。通过 ChatGPT&#xff0c;写作者可以快速生成内容框架、明确研究问题&#xff0c;并优化表达方式。不仅提高了写作效率&…

笔记整理—内核!启动!—kernel部分(1)驱动与内核的关系

首先&#xff0c;恭喜完成了uboot部分的内容整理&#xff0c;其次补充一点&#xff0c;uboot第一部分和第二部分的工作不是一定的&#xff0c;在不同的版本中&#xff0c;可能这个初始化早一点&#xff0c;那个的又放在了第二部分&#xff0c;版本不同&#xff0c;造成的工作顺…

滑动窗口——632. 最小区间

最近在抽时间写LC上的一个专栏——2024春招冲刺百题计划。挑着做&#xff0c;做了几道和滑动窗口相关的题目&#xff0c;632. 最小区间&#xff0c;LC上标记为困难&#xff0c;第一次写完全没有思考&#xff0c;参考了别人写的答案茅塞顿开&#xff0c;特此记录以鞭策自己学习。…

Android perfetto 简介

Android perfetto 简介 使用 perfetto 工具&#xff0c;您可以通过 Android 调试桥 (adb) 在 Android 设备上收集性能信息。使用 adb shell perfetto ... 命令调用 perfetto 工具。 perfetto 从您的设备上收集性能跟踪数据时会使用多种来源&#xff0c;例如&#xff1a; 使用…

5、const修饰类型和变量

const修饰类型和变量 一、const常量一些注意点1、声明时必须同时初始化2、不可更改3、与宏常量的区别 二、const修饰指针1、常量指针constant pointer2、常指向指针pointer-to-constant pointer 一、const常量一些注意点 1、声明时必须同时初始化 #include <iostream> …

框架template初识

框架初识 框架就是一个别人帮我们搭好的舞台&#xff0c;造好了很多现成的工具供我们使用&#xff0c;让开发过程更快速、简洁。 Gin框架介绍 Gin 是一个用 Go (Golang) 编写的 HTTP Web 框架。 Gin是一个用Go语言编写的web框架。它是一个类似于martini 但拥有更好性能的API…

企业网银登录提示请确认您已插入工商银行U盾证书的解决方法

昨天受人之托帮小企业财务解决上网银的问题 因为不是专业做这个的&#xff0c;所以只能安装“常识”行事&#xff0c;但结果实在是令人意想不到。 排错的步骤&#xff1a; 同一台电脑上尝试不同浏览器&#xff0c;发现360浏览器的接受度相当普遍&#xff1b;给U盾换不同的连接…

抖音引流脚本工具,实现自动化引流,解放大家双手

抖音引流脚本工具,实现自动化引流,解放大家双手 #获客引流 推荐阅读&#xff1a; 百收网SEO引流脚本软件不断更新&#xff08;推广全集教程&#xff09;https://www.bsw80.com/post/460.html 我们这篇文章的话&#xff0c;来去给大家说一下这个抖音这个同城评论&#xff0c;…

探索 RAD:5 个最佳实践案例解析

天下武功&#xff0c;唯快不破&#xff01;应用开发&#xff0c;唯速称王&#xff01; 在当今快速发展的科技环境中&#xff0c;企业面临的挑战不断升级。传统的应用开发方法往往因其复杂的流程和较长的开发周期而无法满足快速变化的市场需求。在这种背景下&#xff0c;快速应…

【阿里云】个人认证与公司认证

个人认证和企业认证的区别 更新时间&#xff1a;2024-05-20 09:32:52 本文档主要介绍个人认证账号和企业认证账号的区别。 账号实名认证分为个人实名认证和企业实名认证。 个人账号认证&#xff0c;请选择认证类型为 个人&#xff0c;支持个人支付宝授权认证和个人扫脸认证。…

如何免费将视频转换为 MP4?将视频转换为 MP4 的 5 种方法

您知道如何免费将不受支持的视频格式转换为 MP4 吗&#xff1f; 您可以轻松地在视频共享网站和社交应用上访问各种视频。从网站下载视频但无法使用媒体播放器播放是很常见的事。如何将视频转换为 MP4以便更好地播放&#xff1f; 事实上&#xff0c;市场上有很多视频音频转换工…

【Python 千题 —— 算法篇】字符统计

Python 千题持续更新中 …… 脑图地址 &#x1f449;&#xff1a;⭐https://twilight-fanyi.gitee.io/mind-map/Python千题.html⭐ 题目背景 在编程中&#xff0c;对字符串的字符统计是一个常见任务。这在文本处理、数据分析、词频统计、自然语言处理等领域有广泛应用。无论…

PS插件DR5至臻高级版下载安装教程Photoshop美颜美白牙齿磨皮使用插件百度网盘分享

DR5至臻高级版PS插件DR5至臻高级版下载安装教程Photoshop美颜美白牙齿磨皮使用插件百度网盘分享链接地址可以对眼睛、眉毛、嘴唇等五官进行精细调整&#xff0c;如增强眼睛的明亮度和清晰度&#xff0c;使眉毛更加立体&#xff0c;嘴唇更加饱满可以快速有效地去除人像照片中的瑕…

使用 EMQX 开源版的 Webhook 机制处理消息并存储数据

1、前言 EMQX 是一款强大的开源 MQTT 消息代理&#xff0c;它支持大量的连接和高吞吐量&#xff0c;适用于各种物联网应用。Webhook 是 EMQX 提供的扩展功能之一&#xff0c;用于将消息推送到外部的 HTTP 服务。在本文中&#xff0c;我们将介绍如何使用 EMQX 开源版的 Webhook …

RabbitMQ 03 在项目中的实际使用: 告警,批处理

01.例子&#xff0c;解耦合&#xff08;使用异步&#xff09; 1.1异步思想&#xff1a;不会专门等待 1.2 例子&#xff1a;程序执行 1.3 如何设计程序 多线程&#xff1a; 订单请求模块只用于发送请求和处理确认&#xff0c;订单处理模块专门用于处理请求并且发送确认信…

金税四期工程运维:税务领域的信息化挑战与策略

在信息化浪潮的推动下&#xff0c;中国税务系统迎来了“金税四期”工程这一重大变革。作为税务信息化的新阶段&#xff0c;金税四期不仅标志着税务管理向更高效、更智能的方向迈进&#xff0c;同时也对运维工作提出了前所未有的挑战。本文将从金税四期的背景、运维需求分析、面…

101.对称二叉树

&#xff08;写给未来遗忘的自己&#xff09; 题目&#xff1a; 代码&#xff08;层次递归&#xff1a;&#xff09;&#xff1a; class Solution { public:bool isSymmetric(TreeNode* root) {std::queue<TreeNode*>lefttoright;std::queue<TreeNode*>righttol…

CSP-J基础之进制转换

文章目录 前言数制1. **二进制 (Binary)**2. **八进制 (Octal)**3. **十进制 (Decimal)**4. **十六进制 (Hexadecimal)** K进制转十进制例子 1&#xff1a;以二进制&#xff08;K 2&#xff09;为基数例子 2&#xff1a;以八进制&#xff08;K 8&#xff09;为基数例子 3&…