epoll+线程池模型

news2024/11/16 11:44:08

🔥博客主页: 我要成为C++领域大神
🎥系列专栏:【C++核心编程】 【计算机网络】 【Linux编程】 【操作系统】
❤️感谢大家点赞👍收藏⭐评论✍️

本博客致力于知识分享,与更多的人进行学习交流

负载均衡技术

大量的用户请求可能导致任务分发不均匀,导致资源浪费,不能很好的处理和响应

通过预先设定的分发策略,最大的尝试均匀分发业务,让每台处理机都有任务负载

代理服务器

代理服务器是一个以数据中转为主要职责的中间件,代理服务器可以将用户的请求中转给处理服务器机,也可以将结果反馈给用户,避免用户直接访问服务器主机,提高安全性(安全策略都可以部署在代理服务器中),还可以进行任务的控制与分发,例如负载均衡可以在代理服务器中完成

HA高可用性结构

某个处理机宕机,可用通过HA概念将数据任务转发给正常的处理机。察觉处理机异常,快速反应

线程池设计原则

epoll具有强大的socket监听能力,可以快速察觉所有套接字,线程池具有高并发处理能力,大量的用户请求可用快速处理。

1)提高线程重用性,线程不能与用户绑定,可重复为多个用户处理业务,避免频繁创建销毁线程,减少不必要的开销。

2)预创建,提前准备好部分线程待用,用户发送请求后直接选择线程处理,提高响应速度

3)线程管理策略,设定线程池阈值,通过阈值管理调度线程,线程扩容与缩减

4)为服务器提供并发处理能力,可以更快处理请求或业务

5)提高线程池的重用性,用户实现任务,线程池负责执行任务

6)线程池使用生产者消费者实现,任务传递模式

工作流程

线程池:

生产者将要处理的业务传递到任务队列中去,如果任务队列中有可获取的任务,消费者一直获取执行,生产者投递的任务不允许持续占用消费者。管理者会对消费者进行扫描,根据阈值检测是否需要扩容和缩减,对消费者进行创建或者杀死。

epoll模型:

生产者负责实现epoll模型,将事件转换成业务,投递到线程池中

线程池的扩容和缩减

使用线程池最小阈值min,作为扩容增减数量

扩容:当前任务量>=闲消费者数量 或者 忙线程数量占活线程数量的70%

缩减:当前线程数量+扩容量,小于最大线程阈值

消费者与管理者配合实现缩减

epoll的水平触发模式和边缘触发模式

epoll 的水平触发(Level Triggered, LT)和边缘触发(Edge Triggered, ET)是两种不同的事件通知机制,它们定义了 epoll 如何向应用程序报告文件描述符上的事件。


水平触发(LT)

在水平触发模式下,只要满足条件的事件仍然存在,epoll 就会重复通知这个事件。比如,如果一个文件描述符上有可读数据,那么只要没有读完,epoll_wait 就会不断报告该文件描述符是可读的。这种模式的特点是:

容错性较好,不易丢失事件。

更易于编程和理解。

可以用于多线程程序中,多个线程可以共享同一个 epoll 文件描述符。

缺点:开销大,往返在socket缓冲区和用户之间

在水平模式下,我们的epoll+线程池模型有问题,当第一轮事件监听未处理完毕,epoll_wait不会阻塞,当再次有客户端发送任务时epoll_wait立即返回,并且会对任务进行误添加。

可以以边缘触发模式监听socket的读事件来避免这种问题,node.events=EPOLL | EPOLLET


边缘触发(ET)

边缘触发模式下,事件只在状态变化时被通知一次,之后即使条件仍然满足,也不会再次通知,直到状态再次发生变化。例如,只有当新数据到达使得文件描述符从非可读变为可读时,epoll_wait 才会报告可读事件。边缘触发模式的特点是:

效率更高,因为它减少了事件的重复通知。

需要更加小心地处理每次通知,确保处理所有的数据,否则可能会丢失未处理完的数据。

更适合单线程或者每个线程使用独立 epoll 文件描述符的场景。

代码实现

文件结构:

makefile:

server.h

#ifndef __server_H__
#define __server_H__

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#define TIMEOUT 1
pthread_mutex_t lock;

typedef struct
{
    void *(*bussiness)(void *);//任务函数指针
    void *arg;

}bs_t;

typedef struct
{
    int thread_shutdown;//线程池开关
    int thread_max;//最大线程数
    int thread_min;//最小线程数
    int thread_alive;//有效线程数
    int thread_busy;//忙线程数量
    int kill_number;//缩减码

    bs_t *queue;//任务队列
    int front;
    int rear;
    int cur;
    int max;
    pthread_cond_t Not_Full;
    pthread_cond_t Not_Empty;
    pthread_t * ctids;//存储消费者ID
    pthread_t mtid;//存储管理者ID
}pool_t;//线程池类型

pool_t * thread_pool_create(int Max,int Min,int Qmax);//线程池初始化
int Producer_add_task(pool_t * p,bs_t bs);//生产者添加任务模块,执行一次添加一次任务
void *Customer_thread(void *arg);//消费者线程,参数为线程池地址
void *Manager_thread(void *arg);//管理者线程,参数为线程池地址
int thread_pool_destroy(pool_t *p);//销毁线程池
void * user_bussiness(void *arg);//自定义线程任务
int is_thread_alive(pthread_t pid);

thread_pool_create.c

#include "../include/server.h"


pool_t * thread_pool_create(int Max,int Min,int Qmax)
{
    pool_t *ptr=NULL;
    if((ptr=(pool_t*)malloc(sizeof(pool_t)))==NULL)
    {
        perror("thread_pool_create malloc pool failed");
        exit(0);
    }

    ptr->thread_shutdown=1;
    ptr->thread_max=Max;
    ptr->thread_min=Min;
    ptr->thread_alive=0;
    ptr->kill_number=0;
    ptr->thread_busy=0;
    if((ptr->queue=(bs_t*)malloc(sizeof(bs_t)*Qmax))==NULL)
    {
        perror("thread_pool_create malloc queue failed");
        exit(0);
    }
    
    if((ptr->ctids=(pthread_t*)malloc(sizeof(pthread_t)*Max))==NULL)
    {
        perror("thread_pool_create malloc ctids failed");
        exit(0);
    }
    ptr->front=0;
    ptr->rear=0;
    ptr->cur=0;
    ptr->max=Qmax;

    if(pthread_cond_init(&ptr->Not_Full,NULL)!=0 || pthread_cond_init(&ptr->Not_Empty,NULL)!=0|| pthread_mutex_init(&lock,NULL)!=0)
    {
        printf("thread pool create failed,init Cond or Lock Failed\n");
        exit(0);
    }

    int err;
    for(int i=0;i<Min;++i)
    {
        if((err=pthread_create(&ptr->ctids[i],NULL,Customer_thread,(void*)ptr))!=0)
        {
            printf("thread pool create failed,customer thread create failed:%s",
                    strerror(err));
            exit(0);
        }
        ++ptr->thread_alive;
    }
        if((err=pthread_create(&ptr->mtid,NULL,Manager_thread,(void*)ptr))!=0)
        {
            printf("thread pool create failed,manager thread create failed:%s",strerror(err));
            exit(0);
        }
        
        pthread_create(&ptid,NULL,print_thread,(void*)ptr);
        printf("Print Thread Create Success...\n");
        return ptr;
}

thread_pool_destroy.c

#include "../include/server.h"

int thread_pool_destroy(pool_t *p)
{
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&p->Not_Full);
    pthread_cond_destroy(&p->Not_Empty);
    free(p->ctids);
    free(p->queue);
    free(p);
    return 0;
}

Epoll_Listen.c

#include "../include/server.h"

int Epoll_Listen(int serverfd,pool_t * p)
{
    struct epoll_event ready_array[EPOLLMAX];
    int ready,flag;
    bs_t tmp;
    printf("Epoll_Thread Server,Epoll_Listen Running...\n");
    while(p->thread_shutdown)
    {
        if((ready=epoll_wait(epfd,ready_array,EPOLLMAX,-1))==-1)
        {
            perror("Epoll_Listen call failed,epoll_wait call failed");
            exit(0);
        }
        flag=0;
        while(ready)
        {
            if(ready_array[flag].data.fd==serverfd)
            {
                tmp.business=Business_Accept;
                tmp.arg=(void *)&serverfd;
                Producer_add_task(p,tmp);
            }
            else
            {
                tmp.business=Business_Retime;
                tmp.arg=((void*)&ready_array[flag].data.fd);
                Producer_add_task(p,tmp);
            }
            ++flag;
            --ready;
        }
    }
    close(serverfd);
    return 0;
}

Manager_thread.c

#include "../include/server.h"

void * Manager_thread(void *arg)
{
    pthread_detach(pthread_self());
    pool_t *p=(pool_t *)arg;
    int alive,cur,busy;
    pthread_mutex_lock(&lock);
    alive=p->thread_alive;
    busy=p->thread_busy;
    cur=p->cur;
    pthread_mutex_unlock(&lock);
    int add,flag;
    //持续执行
    while(p->thread_shutdown)
    {
        if((cur>=alive-busy||(double)busy/alive*100>=70)&&alive+p->thread_min<=p->thread_max)
        {
            for(flag=0,add=0;flag<p->thread_max&&add<p->thread_min;flag++)
            {
                if(p->ctids[flag]==0 || !is_thread_alive(p->ctids[flag]))
                {
                    pthread_create(&p->ctids[flag],NULL,Customer_thread,(void*)p);
                    add++;
                    pthread_mutex_lock(&lock);
                    ++(p->thread_alive);
                    pthread_mutex_unlock(&lock);
                }
            }
            pthread_kill(ptid,SIGUSR1);
        }
        if(busy*2<=alive-busy && alive-p->thread_min >= p->thread_min)
        {
            printf("%d\n",p->thread_min);
            pthread_mutex_lock(&lock);
            p->kill_number=p->thread_min;
            pthread_mutex_unlock(&lock);
            for(int i=0;i<p->thread_min;++i)
            {
                pthread_cond_signal(&p->Not_Empty);
            }
            
        }
        sleep(TIMEOUT);
    }
        printf("Thread shutdown 0,manager thread[0x%x]exiting...\n",
                (unsigned int)pthread_self());
        pthread_exit(NULL);
}

Business_Retime.c

#include "../include/server.h"

void * Business_Retime(void *arg)
{
    int toupper_flag=0;
    char recv_buf[1024];
    bzero(recv_buf,sizeof(recv_buf));
    char time_buf[100];
    bzero(time_buf,sizeof(time_buf));
    time_t tp;
    int recvlen;
    int sockfd=*(int*)arg;
    printf("111\n");
    while((recvlen=recv(sockfd,recv_buf,sizeof(recv_buf),MSG_DONTWAIT))==-1)
    {
        if(errno==EINTR)
        break;
        perror("Business_Retime recv call failed");
        exit(0);
    }

    if(recvlen>0)
    {
        if (strcmp(recv_buf, "localtime") == 0) {
               tp = time(NULL); // 获取时间种子
               ctime_r(&tp, time_buf);
               time_buf[strcspn(time_buf, "\n")] = '\0';
               send(sockfd, time_buf, strlen(time_buf) + 1, MSG_NOSIGNAL);
               bzero(time_buf, sizeof(time_buf));
               } 
	else 
	{
                toupper_flag = 0;
                while (recvlen > toupper_flag) 
		{
                recv_buf[toupper_flag] = toupper(recv_buf[toupper_flag]);
                ++toupper_flag;
          	}
                send(sockfd, recv_buf, recvlen, MSG_NOSIGNAL);
                bzero(recv_buf, sizeof(recv_buf));
    }
    }
    else if(recvlen==0)
    {
        close(sockfd);
        epoll_ctl(epfd,EPOLL_CTL_DEL,sockfd,NULL);
    }

    return NULL;
}

Epoll_Create.c

#include "../include/server.h"

int Epoll_Create(int serverfd)
{
    int epfd;
    if((epfd=epoll_create(EPOLLMAX))==-1)
    {
        perror("Epoll_Create call failed,epoll_create call failed");
        exit(0);
    }
    struct epoll_event node;
    node.data.fd=serverfd;
    node.events=EPOLLIN|EPOLLET;
    if((epoll_ctl(epfd,EPOLL_CTL_ADD,serverfd,&node))==-1)
    {
        perror("Epoll_Create call failed,epoll_ctl call failed");
        exit(0);
    }
    printf("Epoll_Server Epoll Create success...\n");
    return epfd;

}

Business_Accept.c

#include "../include/server.h"

void *Business_Accept(void *arg)
{
    struct sockaddr_in addr;
    socklen_t addrlen;
    int sockfd=*(int *)arg;
    int customerfd;
    struct epoll_event node;
    char response[1024];
    char ip[16];
    bzero(response,sizeof(response));
    bzero(ip,sizeof(ip));
    addrlen=sizeof(addr);
    if((customerfd=accept(sockfd,(struct sockaddr*)&addr,&addrlen))==-1)
    {
        perror("Business_Accept accept call failed");
        exit(0);
    }
    inet_ntop(AF_INET,&addr.sin_addr.s_addr,ip,16);
    node.data.fd=customerfd;
    node.events=EPOLLIN|EPOLLET;
    if(epoll_ctl(epfd,EPOLL_CTL_ADD,customerfd,&node)==-1)
    {
        perror("Business_Accept epoll_ctl call failed");
        exit(0);
    }
    sprintf(response,"hi Thread [%s] welcome to epoll demo",ip);
    send(customerfd,response,strlen(response),MSG_NOSIGNAL);
    return NULL;
}

print_thread.c

#include "../include/server.h"

void *print_thread(void*arg)
{
    pthread_detach(pthread_self());
    pool_t *ptr=(pool_t*)arg;
    PTR=ptr;
    struct sigaction act,oact;
    act.sa_handler=sig_usr;
    act.sa_flags=0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGUSR1,&act,&oact);//设置捕捉
    sigprocmask(SIG_SETMASK,&act.sa_mask,NULL);//解除屏蔽
    while(ptr->thread_shutdown)
        sleep(TIMEOUT);//等待信号
    pthread_exit(NULL);
}

sig_usr.c

#include "../include/server.h"

void sig_usr(int n)
{
    //显示一次阈值信息
    printf("[Thread_Epoll_Server Info] alive[%d] busy[%d] Idel[%d] Cur[%d] Busy/Alive[%.2f%%] Alive/max[%.2f%%]\n",
PTR->thread_alive,PTR->thread_busy,PTR->thread_alive-PTR->thread_busy,PTR->cur,(double)PTR->thread_busy/PTR->thread_alive*100,(double)PTR->thread_alive/PTR->thread_max*100);
} 

Producer_add_task.c

#include "../include/server.h"

int Producer_add_task(pool_t *p,bs_t bs)
{
    if(p->thread_shutdown)
    {
        //上锁
        pthread_mutex_lock(&lock);
        while(p->cur==p->max)
        {
            pthread_cond_wait(&p->Not_Full,&lock);
            if(!p->thread_shutdown)
            {
                pthread_mutex_unlock(&lock);
                printf("thread shutdown 0,exiting...\n");
                pthread_exit(NULL);
            }
        }
        //添加一个业务
        p->queue[p->front].business=bs.business;
        p->queue[p->front].arg=bs.arg;
        ++p->cur;
        p->front=(p->front+1)%p->max;
        //解锁
        pthread_mutex_unlock(&lock);
        pthread_kill(ptid,SIGUSR1);
        //唤醒一个消费者
        pthread_cond_signal(&p->Not_Empty);
    }
    else
    {
        printf("thread shutdown 0,exiting...\n");
        pthread_exit(NULL);
    }
        printf("Producer Thread [0x%x] Add Task Successfully,business_addr=%p\n",
               (unsigned int)pthread_self(),bs.business);
    return 0;
}

Net_init.c

#include "../include/server.h"

int Net_init(void)
{
    int sockfd;
    struct sockaddr_in sockAddr;
    bzero(&sockAddr,sizeof(sockAddr));
    sockAddr.sin_family=AF_INET;
    sockAddr.sin_port=htons(8080);
    sockAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        perror("socket call failed");
        exit(0);
    }
    if(bind(sockfd,(struct sockaddr*)&sockAddr,sizeof(sockAddr))==-1)
    {

        perror("bind call failed");
        exit(0);
    }
    listen(sockfd,BACKLOG);
    printf("Epoll_thread Server Net init Success...\n");
    return sockfd;
}

is_thread_alive.c

#include "../include/server.h"

int is_thread_alive(pthread_t tid)
{
    pthread_kill(tid,0);
    if(errno==ESRCH)
        return 0;
    return 1;

}

main.c

#include "../include/server.h"

int main(void)
{
    //主线程设置对SIGUSR1信号的屏蔽,继承给所有线程
    sigset_t set,oset;
    sigemptyset(&set);
    sigaddset(&set,SIGUSR1);
    sigprocmask(SIG_SETMASK,&set,&oset);
    //启动接口start
    int sockfd=Net_init();
    epfd=Epoll_Create(sockfd);
    pool_t *ptr=thread_pool_create(300,10,1000);
    Epoll_Listen(sockfd,ptr);
    if(!ptr->thread_shutdown) 
        thread_pool_destroy(ptr);
    printf("Epoll Server Closing...\n");
    return 0;
}

运行结果

在服务端刚启动后,有13个线程,一个生产者,十个消费者,一个管理者,一个输出线程

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

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

相关文章

AI工具 GPT 学术优化 (GPT Academic) 安装实践

GPT 学术优化 (GPT Academic)是一个综合的AI GPT工具包&#xff0c;可以完成各种gpt辅助的工作&#xff0c;比如代码解读、翻译、读论文等功能。官网&#xff1a;GitHub - binary-husky/gpt_academic: 为GPT/GLM等LLM大语言模型提供实用化交互接口&#xff0c;特别优化论文阅读…

Flutter 自动化测试 -appium-flutter-driver

上篇文章有讲述如何通过FlutterDriver实现集成测试Flutter 应用自动化测试-集成测试篇 不熟悉的小伙伴可以先去看看。 什么是Appium Flutter Driver&#xff1f; 作为Flutter开发&#xff0c;FlutterDriver是足够帮助他们进行测试的&#xff0c;而作为自动化测试工程师最大的困…

FFmpeg的入门实践系列五(编程入门之属性查看)

欢迎诸位来阅读在下的博文~ 在这里&#xff0c;在下会不定期发表一些浅薄的知识和经验&#xff0c;望诸位能与在下多多交流&#xff0c;共同努力 文章目录 前期博客参考书籍一、AVFormatContext结构体1. 结构定义2. 字段说明3.示例1&#xff08;打开与关闭音视频文件&#xff0…

机器人学——机械臂轨迹规划-1

引言 理想轨迹 步骤-1 步骤-2 笛卡尔空间下的轨迹规划 步骤-1 步骤-2 三次多项式 矩阵形式求解 det(T): 行列式&#xff0c;非齐次多项式&#xff0c;结果不为零&#xff0c;有唯一解、行列式为零&#xff08;无穷解/无解&#xff0c;还需查看增广矩阵的秩&#xff09; 速度…

Linux网络编程:多路转接--select

1. 初识select 系统提供select函数来实现多路复用输入/输出模型. select系统调用是用来让我们的程序监视多个文件描述符的状态变化的; 程序会停在select这里等待&#xff0c;直到被监视的文件描述符有一个或多个发生了状态改变 select只负责等待&#xff0c;可以等待多个fd&a…

内容创作者福音,4款文章改写神器轻松提升文章质量

在信息爆炸的时代&#xff0c;内容创作成为了连接世界的重要桥梁。作为一名专业创作者&#xff0c;我深知保持内容原创性和高质量的重要性。然而&#xff0c;灵感有时会枯竭&#xff0c;改写文章成为一项耗时且艰巨的任务。幸运的是&#xff0c;市面上有一些文章改写神器&#…

Flask+LayUI开发手记(四):弹出层实现增删改查功能

在上一节用dataTable实现数据列表时&#xff0c;已经加了表头工具栏和表内工具栏&#xff0c;栏内的按钮功能都是用来完成数据的增删改查了&#xff0c;这又分成两类功能&#xff0c;一类是删除或设置&#xff0c;这类功能简单&#xff0c;只需要选定记录&#xff0c;然后提交到…

Flutter 自动化测试 - 集成测试篇

Flutter集成测试 Flutter官方对Flutter应用测试类型做了三个阶段划分&#xff0c;分别为Unit&#xff08;单元&#xff09;测试、Widget&#xff08;组件&#xff09;测试、Integration&#xff08;集成&#xff09;测试。按照维护成本来看的话从左到右依次增高&#xff0c;按照…

预测癌症免疫治疗反应-TIDE数据库学习及知识整理

TIDE&#xff08;Tumor Immune Dysfunction and Exclusion&#xff09; 是一个用于预测癌症患者对免疫检查点抑制剂&#xff08;如PD-1/PD-L1抑制剂&#xff09;反应的算法。研究者通过检测肿瘤建模队列中每个基因的表达与效应性毒性T淋巴细胞(CTL)浸润水平的相互关系及对生存情…

Open3D 近似点体素滤波(36)

Open3D 近似点体素滤波(36) 一、算法介绍二、算法实现1.代码2.效果一、算法介绍 这个算法也是体素滤波, 它保留的点是近似点,也就是新的点,原始点云中对应位置是不存在这些点的。其他的看着类似,下面是代码,滤波抽稀结果 二、算法实现 1.代码 代码如下(示例): …

学习文件IO,让你从操作系统内核的角度去理解输入和输出(Java实践篇)

本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. &#x1f92d;&#x1f92d;&#x1f92d;可能说的不是那么严谨.但小编初心是能让更多人…

【在Linux世界中追寻伟大的One Piece】应用层协议HTTP

目录 1 -> HTTP协议 2 -> 认识URL 2.1 -> urlencode和urldecode 3 -> HTTP协议请求与响应格式 3.1 -> HTTP请求 3.2 -> HTTP响应 4 -> HTTP的方法 4.1 -> HTTP常见方法 5 -> HTTP的状态码 6 -> HTTP常见Header 7 -> 最简单的HTTP服…

Linux系统报错“version ‘GLIBC_2.34‘ not found”解决方法

注意&#xff0c;此文章慎用&#xff0c;glibc不可随意升级&#xff0c;可能导致系统崩溃 一、查看版本 ldd --version 二、添加高版本源 sudo vi /etc/apt/sources.list.d/my.list 进入编辑页面 "i"键进入插入模式 输入源 deb http://th.archive.ubuntu.com/…

【信创】推荐一款超级好用的文件同步备份工具 _ 统信 _ 麒麟 _ 方德

往期好文&#xff1a;【信创】统信UOS打包工具介绍与使用教程 Hello&#xff0c;大家好啊&#xff01;今天给大家推荐一款在Linux系统上超级好用的文件同步和备份工具——FreeFileSync。无论是在日常工作还是数据管理中&#xff0c;文件同步和备份都是至关重要的任务。FreeFile…

【自动驾驶】控制算法(五)连续方程离散化与离散LQR原理

写在前面&#xff1a; &#x1f31f; 欢迎光临 清流君 的博客小天地&#xff0c;这里是我分享技术与心得的温馨角落。&#x1f4dd; 个人主页&#xff1a;清流君_CSDN博客&#xff0c;期待与您一同探索 移动机器人 领域的无限可能。 &#x1f50d; 本文系 清流君 原创之作&…

QT6 setCentralWidget 和 takeCentralWidget

qt6 中&#xff0c;初始化界面完成之后&#xff0c;可以使用setCentralWidget 设置当前的widget为中心页面 如果你存在多个widget想要多个切换 如果存在widget1 和 widget2 在初始化的时候 setCentralWidget(widget1)触发操作切换到 widget2 如果没有先takeCentralWidget 直…

13.深入解析ThreadPoolExecutor线程池

ThreadPoolExecutor线程池 线程池简介线程池的使用创建线程池ThreadPoolExecutor——推荐使用线程池的核心参数 Executors——不推荐使用 提交任务如何执行批量任务如何执行定时、延时任务如何执行周期、重复性任务 关闭线程池线程池的参数设计分析核心线程数(corePoolSize)最大…

EEMD-MPE-KPCA-BiLSTM、EEMD-MPE-BiLSTM、EEMD-PE-BiLSTM故障识别、诊断(Matlab)

EEMD-MPE-KPCA-BiLSTM(集合经验分解-多尺度排列熵-核主元分析-双向长短期网络)故障识别、诊断&#xff08;Matlab) 目录 EEMD-MPE-KPCA-BiLSTM(集合经验分解-多尺度排列熵-核主元分析-双向长短期网络)故障识别、诊断&#xff08;Matlab)效果一览基本介绍程序设计参考资料 效果一…

RK3588人工智能开发----【1】初识NPU

NPU 的诞生&#xff01; 随着人工智能和大数据时代的到来&#xff0c;传统嵌入式处理器中的CPU和GPU逐渐无法满足日益增长的深度学习需求。为了应对这一挑战&#xff0c;在一些高端处理器中&#xff0c;NPU&#xff08;神经网络处理单元&#xff09;也被集成到了处理器里。NPU的…

【GNSS射频前端】MA2769初识

MAX2769 芯片概述&#xff1a; MAX2769是一款单芯片多系统GNSS接收器&#xff0c;采用Maxim的低功耗SiGe BiCMOS工艺技术。集成了包括双输入低噪声放大器&#xff08;LNA&#xff09;、混频器、图像拒绝滤波器、可编程增益放大器&#xff08;PGA&#xff09;、压控振荡器&#…