Linux--IO多路复用(select,poll,epoll)

IO多路复用——select,poll,epoll

IO多路复用是一种操作系统技术,旨在提高系统处理多个输入输出操作的性能和资源利用率。与传统的多线程或多进程模型相比,IO多路复用避免了因阻塞IO而导致的资源浪费和低效率问题。它通过将多个IO操作合并到一个系统调用中,允许程序同时等待多个文件描述符(如sockets、文件句柄等)变为可读或可写状态,然后再执行实际的IO操作。

在IO多路复用的实现中,常用的系统调用包括select()poll()epoll()。这些机制允许程序监视多个描述符,一旦某个描述符就绪(通常是读就绪或写就绪),程序就会被通知进行相应的读写操作。这个过程通常涉及两个阶段:

  1. 等待数据到达:程序等待数据从IO设备传输到内核空间。在这个阶段,IO多路复用的系统调用会阻塞,直到至少有一个描述符准备好进行IO操作。

  2. 数据复制:当一个或多个描述符就绪时,程序负责将数据从内核空间复制到用户空间(进程或线程的缓冲区)。这第二个阶段是实际的读写操作,它在IO多路复用的上下文中是同步的,因为程序需要自己执行数据的读写。

尽管select()poll()epoll()都是同步IO操作,但它们提供了一种有效的方式来处理并发IO,降低了系统开销,并提高了并发处理能力。与此不同,异步IO(AIO)模型进一步简化了IO操作,因为它允许操作系统自动处理数据从内核到用户空间的复制过程,无需程序显式调用读写操作。这意味着在异步IO模型中,读写操作由操作系统在后台完成,从而进一步提高了应用程序的效率和响应性。

a411a9791264d372df052436f83741e8.png

select

概述

  • 系统提供了select函数来实现多路复用输入/输出模型

  • select系统调用是用来让我们的程序监视多个文件描述符的状态变化的

  • 程序会停在select函数等待,直到被监视的文件描述符有一个或者多个发生了状态改变。

c03874c465c6dc86eb3b53af27bd076b.png

函数

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
  • 函数参数:

参数说明
nfds是需要监视的最大的文件描述符值+1
readfds需要检测的可读文件描述符的集合
writefds需要检测的可写文件描述符的集合
exceptfds需要检测的异常文件描述符的集合
timeout当timeout等于NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件;
当timeout为0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
当timeout为特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。
返回——
> 0返回文件描述词状态已改变的个数
== 0代表在描述词状态改变前已超过timeout时间,没有返回
< 0错误原因存于errno,此时参数readfds,writefds, exceptfds和timeout的值变成不可预测,错误值可能为:
EBADF:文件描述词为无效的或该文件已关闭
EINTR:此调用被信号所中断
EINVAL:参数n 为负值
ENOMEM:核心内存不足
  • 其中:可读,可写,异常文件描述符的集合是一个fd_set类型,fd_set是系统提供的位图类型,位图的位置是否是1,表示是否关系该事件。例如:

输入时:假如我们要关心 0 1 2 3 文件描述符0000 0000->0000 1111 比特位的位置,表示文件描述符的编号比特位的内容 0 or 1 表示是否需要内核关心
输出时:0000 0100->此时表示文件描述符的编号比特位的内容 0 or 1哪些用户关心的fd 上面的读事件已经就绪了,这里表示2描述符就绪了
  • 系统提供了关于fd_set的接口,便于我们使用位图:

void FD_CLR(int fd, fd_set *set); // 用来清除描述词组set中相关fd 的位
int FD_ISSET(int fd, fd_set *set); // 用来测试描述词组set中相关fd 的位是否为真
void FD_SET(int fd, fd_set *set); // 用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set *set); // 用来清除描述词组set的全部位

执行流程:

  1. 执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。

  2. 若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1) 。

  3. 若再加入fd=2,fd=1,则set变为0001,0011 。

  4. 执行select(6,&set,0,0,0)阻塞等待,表示最大文件描述符+1是6,监控可读事件,立即返回。

  5. 若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

优缺点

  • 优点:

  1. 可监控的文件描述符个数取决与sizeof(fd_set)的值。一般大小是1024,但是fd_set的大小可以调整。

  2. 将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd。①是用于再select 返回后,array作为源数据和fd_set进行FD_ISSET判断。②是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。

缺点:

  1. 每次调用select, 都需要手动设置fd集合, 从接口使用角度来说也非常不便。

  2. 每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。

  3. 同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。

  4. select支持的文件描述符数量太小。

实例

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/select.h>const static int MAXLINE = 1024;
const static int SERV_PORT = 10001;int main()
{int i , maxi , maxfd, listenfd , connfd , sockfd ;/*nready 描述字的数量*/int nready ,client[FD_SETSIZE];int n ;/*创建描述字集合,由于select函数会把未有事件发生的描述字清零,所以我们设置两个集合*/fd_set rset , allset;char buf[MAXLINE];socklen_t clilen;struct sockaddr_in cliaddr , servaddr;/*创建socket*/listenfd = socket(AF_INET , SOCK_STREAM , 0);/*定义sockaddr_in*/memset(&servaddr , 0 ,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);bind(listenfd, (struct sockaddr *) & servaddr , sizeof(servaddr));listen(listenfd , 100);/*listenfd 是第一个描述字*//*最大的描述字,用于select函数的第一个参数*/maxfd = listenfd;/*client的数量,用于轮询*/maxi = -1;/*init*/for(i=0 ;i<FD_SETSIZE ; i++)client[i] = -1;FD_ZERO(&allset);FD_SET(listenfd, &allset);for (;;){rset = allset;/*只select出用于读的描述字,阻塞无timeout*/nready = select(maxfd+1 , &rset , NULL , NULL , NULL);if(FD_ISSET(listenfd,&rset)){clilen = sizeof(cliaddr);connfd = accept(listenfd , (struct sockaddr *) & cliaddr , &clilen);/*寻找第一个能放置新的描述字的位置*/for (i=0;i<FD_SETSIZE;i++){if(client[i]<0){client[i] = connfd;break;}}/*找不到,说明client已经满了*/if(i==FD_SETSIZE){printf("Too many clients , over stack .\n");return -1;}FD_SET(connfd,&allset);//设置fd/*更新相关参数*/if(connfd > maxfd) maxfd = connfd;if(i>maxi) maxi = i;if(nready<=1) continue;else nready --;}for(i=0 ; i<=maxi ; i++){if (client[i]<0) continue;sockfd = client[i];if(FD_ISSET(sockfd,&rset)){n = read(sockfd , buf , MAXLINE);if (n==0){/*当对方关闭的时候,server关闭描述字,并将set的sockfd清空*/close(sockfd);FD_CLR(sockfd,&allset);client[i] = -1;}else{buf[n]='\0';printf("Socket %d said : %s\n",sockfd,buf);write(sockfd,buf,n); //Write back to client}nready --;if(nready<=0) break;}}}return 0;
}

poll

概述

  • poll和select实现原理基本类似

  • poll只为了解决select的两个硬伤:①等待的fd是有上限的,(底层类似链表储存实现,而不是位图)。②每次要对关心的fd进行事件重置,(pollfd结构包含了要监视的event和发生的event,使用前后不用初始化fd_set)。

函数

int poll(struct pollfd *fds, nfds_t nfds, int timeout);// pollfd结构
struct pollfd {int fd; /* file descriptor */short events; /* requested events */short revents; /* returned events */
};
  • 函数参数:

参数说明
fds是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合
nfds表示fds数组的长度
timeout表示poll函数的超时时间, 单位是毫秒(ms)
返回——
> 0表示poll由于监听的文件描述符就绪而返回
== 0表示poll函数等待超时
< 0表示出错

优缺点

  • 优点:

  1. pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式. 接口使用比 select更方便。

  2. poll并没有最大数量限制 (但是数量过大后性能也是会下降)。

缺点:

  1. 和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。

  2. 每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中。

  3. 同时连接的大量客户端在一时刻可能只有很少的处于就绪状态, 因此随着监视的描述符数量的增长, 其效率也会线性下降。

实例

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <poll.h>#define MAXLINE  1024
#define OPEN_MAX  16 //一些系统会定义这些宏
#define SERV_PORT  10001int main()
{int i , maxi ,listenfd , connfd , sockfd ;int nready;int n;char buf[MAXLINE];socklen_t clilen;struct pollfd client[OPEN_MAX];struct sockaddr_in cliaddr , servaddr;listenfd = socket(AF_INET , SOCK_STREAM , 0);memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);bind(listenfd , (struct sockaddr *) & servaddr, sizeof(servaddr));listen(listenfd,10);client[0].fd = listenfd;client[0].events = POLLRDNORM;for(i=1;i<OPEN_MAX;i++){client[i].fd = -1;}maxi = 0;for(;;){nready = poll(client,maxi+1,INFTIM);if (client[0].revents & POLLRDNORM){clilen = sizeof(cliaddr);connfd = accept(listenfd , (struct sockaddr *)&cliaddr, &clilen);for(i=1;i<OPEN_MAX;i++){if(client[i].fd<0){client[i].fd = connfd;client[i].events = POLLRDNORM;break;}}if(i==OPEN_MAX){printf("too many clients! \n");}if(i>maxi) maxi = i;nready--;if(nready<=0) continue;}for(i=1;i<=maxi;i++){if(client[i].fd<0) continue;sockfd = client[i].fd;if(client[i].revents & (POLLRDNORM|POLLERR)){n = read(client[i].fd,buf,MAXLINE);if(n<=0){close(client[i].fd);client[i].fd = -1;}else{buf[n]='\0';printf("Socket %d said : %s\n",sockfd,buf);write(sockfd,buf,n); //Write back to client}nready--;if(nready<=0) break; //no more readable descriptors}}}return 0;
}

epoll

概述

  • epoll:是为处理大批量句柄而作了改进的poll(真的是大改进)

  • epoll是IO多路复用技术,在实现上维护了一个用于返回触发事件的Socket的链表和一个记录监听事件的红黑树,epoll的高效体现在:

  1. 对监听事件的修改是 logN(红黑树)。

  2. 用户程序无需遍历所有的Socket(发生事件的Socket被放到链表中直接返回)。

  3. 内核无需遍历所有的套接字,内核使用回调函数在事件发生时直接转到对应的处理函数。

8dbb14ec649a268aecc8256acaf71f27.png

函数

  • epoll_create:创建一个epoll的句柄,用完之后, 必须调用close()关闭。

int epoll_create(int size);
  • epoll_ctl:它不同于select()是在监听事件时告诉内核要监听什么类型的事件, 而是在这里先注册要监听的事件类型。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);typedef union epoll_data
{void *ptr;int fd;uint32_t u32;uint64_t u64;
} epoll_data_t;struct epoll_event
{uint32_t events;epoll_data_t data;
} EPOLL_PACKED;
  1. events参数的宏集合:

EPOLLIN : 表示对应的文件描述符可以读 (包括对端SOCKET正常关闭)。
EPOLLOUT : 表示对应的文件描述符可以写。
EPOLLPRI : 表示对应的文件描述符有紧急的数据可读 (这里应该表示有带外数据到来)。
EPOLLERR : 表示对应的文件描述符发生错误。
EPOLLHUP : 表示对应的文件描述符被挂断。
EPOLLET : 将EPOLL设为边缘触发(Edge Triggered)模式, 这是相对于水平触发(Level Triggered)来说的。
EPOLLONESHOT:只监听一次事件, 当监听完这次事件之后, 如果还需要继续监听这个socket的话, 需要再次把这个socket加入到EPOLL队列里
  1. 函数参数:

参数说明
epfdepoll_create()的返回值(epoll的句柄)
op表示动作,用三个宏来表示:
EPOLL_CTL_ADD :注册新的fd到epfd中
EPOLL_CTL_MOD :修改已经注册的fd的监听事件
EPOLL_CTL_DEL :从epfd中删除一个fd
fd需要监听的fd
event内核需要监听的事件
  • epoll_wait:收集在epoll监控的事件中已经发送的事件

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
参数说明
epfdepoll_create()的返回值(epoll的句柄)
events是分配好的epoll_event结构体数组。epoll将会把发生的事件赋值到events数组中 (events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)
maxevents通知内核这个events有多大,这个maxevents的值不能大于创建epoll_create()时的size
timeout超时时间 (毫秒,0会立即返回,-1是永久阻塞)
返回——
> 0返回对应I/O上已准备好的文件描述符数目
== 0表示已超时
< 0表示失败

执行流程:

6c287cb29f3ab1f41e4283b5269b21be.png

  1. 当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。

  2. 每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。

  3. 这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。

  4. 而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当响应的事件发生时会调用这个回调方法。

  5. 这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。

  6. 在epoll中,对于每一个事件,都会建立一个epitem结构体。

  7. 当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。

  8. 如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户. 这个操作的时间复杂度是O(1)。

优缺点

  • 优点:

  1. 接口使用方便: 虽然拆分成了三个函数,但是反而使用起来更方便高效,不需要每次循环都设置关注的文件描述符,也做到了输入输出参数分离开。

  2. 数据拷贝轻量: 只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中,这个操作并不频繁(而select/poll都是每次循环都要进行拷贝)。

  3. 事件回调机制: 避免使用遍历,而是使用回调函数的方式,将就绪的文件描述符结构加入到就绪队列中,epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪,这个操作时间复杂度O(1),即使文件描述符数目很多,效率也不会受到影响。

  4. 没有数量限制: 文件描述符数目无上限。

缺点:

  1. 不能跨平台,epoll 是 Linux 特有的 API,不太容易移植到其他操作系统上

实例

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <poll.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/resource.h>#define MAXLINE  1024
#define OPEN_MAX  16 //一些系统会定义这些宏
#define SERV_PORT  10001int main()
{int i , maxi ,listenfd , connfd , sockfd ,epfd, nfds;int n;char buf[MAXLINE];struct epoll_event ev, events[20];  socklen_t clilen;struct pollfd client[OPEN_MAX];struct sockaddr_in cliaddr , servaddr;listenfd = socket(AF_INET , SOCK_STREAM , 0);memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);servaddr.sin_addr.s_addr = htonl(INADDR_ANY);bind(listenfd , (struct sockaddr *) & servaddr, sizeof(servaddr));listen(listenfd,10);epfd = epoll_create(256);ev.data.fd=listenfd; ev.events=EPOLLIN|EPOLLET;epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);for(;;){nfds=epoll_wait(epfd,events,20,500); for(i=0; i<nfds; i++){if (listenfd == events[i].data.fd){clilen = sizeof(cliaddr);connfd = accept(listenfd , (struct sockaddr *)&cliaddr, &clilen);if(connfd < 0)  {  perror("connfd < 0");  exit(1);  }ev.data.fd=connfd; ev.events=EPOLLIN|EPOLLET;epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);                }else if (events[i].events & EPOLLIN){if ( (sockfd = events[i].data.fd) < 0)  continue;  n = recv(sockfd,buf,MAXLINE,0);if (n <= 0)   {    close(sockfd);  events[i].data.fd = -1;  }else{buf[n]='\0';printf("Socket %d said : %s\n",sockfd,buf);ev.data.fd=sockfd; ev.events=EPOLLOUT|EPOLLET;epoll_ctl(epfd,EPOLL_CTL_MOD,connfd,&ev);}}else if( events[i].events&EPOLLOUT ){sockfd = events[i].data.fd;  send(sockfd, "Hello!", 7, 0);  ev.data.fd=sockfd;  ev.events=EPOLLIN|EPOLLET;  epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); }else {printf("This is not avaible!");}}}close(epfd);  return 0;
}

总结

  • selectpoll 是两种传统的 I/O 多路复用技术,它们允许服务器应用程序同时监控多个网络连接,以便在连接准备就绪时进行读写操作。尽管这两种技术在处理大量并发连接时非常有用,但随着连接数的增加,它们的性能会逐渐下降,因为它们需要在每次调用时遍历整个文件描述符集合,这在连接数非常多时会导致效率问题。

  • 为了解决这个问题,epoll 作为 selectpoll 的一种改进方案,在 Linux 系统中被引入。epoll 提供了一种更为高效的事件驱动模型,它可以显著提高处理大量并发连接的性能。与 selectpoll 不同,epoll 不会对整个文件描述符集合进行线性遍历,而是使用一组特殊的数据结构来跟踪哪些文件描述符已经准备好 I/O 操作。这种机制使得 epoll 能够快速地通知应用程序哪些连接是活跃的,而无需对所有连接进行不必要的检查。

  • epoll 的另一个优点是它能够处理大量文件描述符而不会显著增加资源消耗,这使得它非常适合需要处理成千上万甚至更多并发连接的高性能网络服务器。因此,在 Linux 系统上,epoll 常被视为 selectpoll 的替代方案,特别是在构建高性能网络应用程序时。

- END -

往期推荐:点击图片即可跳转阅读

dd2e621c8781f0abdcc8578f074741bc.jpeg

《linux--sysfs文件系统》

f84ff51a6c863e2f55ce975e1b8269b5.jpeg

《YY3568 Debian11+RT-Thread混合内核部署》

f4bdcfd3ea5deb2cbcb96dea5c4ac460.jpeg

《YY3568多核异构(Linux+RT-Thread)--启动流程》

9fc8bb14e4398c7a5cf31450fd857e95.png

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

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

相关文章

Linux awk案例

目录 1. 查询时间超过2000毫秒的请求2. 查询指定列组合出现的次数3. 统计所有文件的大小4. 获取大于指定大小的文件名&#xff0c;并按照从大到小排序5. grep指定字段后&#xff0c;使用awk列转行6. 查询第四个字段等于指定值的内容 1. 查询时间超过2000毫秒的请求 ✅log: 202…

[Leetcode 216][Medium]组合总和 III--回溯

目录 一、题目描述 二、整体思路 三、代码 一、题目描述 原题地址 二、整体思路 对于组合问题&#xff0c;首先要想到回溯法。那么可以根据回溯法模版进行设计。 void backtrace(元素){if(满足题目要求的条件){保存目前路径/状态/结果;return;}for循环,往目前状态相邻的所…

区块链通证系统功能分析

区块链通证系统功能分析涉及多个关键方面&#xff0c;以确保系统能够满足不同的业务需求和合规性要求。 同质与非同质通证&#xff1a;区块链通证系统需要支持同质通证&#xff08;如ERC-20&#xff09;和非同质通证&#xff08;如ERC-721&#xff09;&#xff0c;以适应不同类…

《NLP自然语言处理》—— 关键字提取之TF-IDF算法

文章目录 一、TF-IDF算法介绍二、举例说明三、示例&#xff1a;代码实现四、总结 一、TF-IDF算法介绍 TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xff09;是一种用于信息检索与文本挖掘的常用加权技术。TF-IDF是一种统计方法&#xff0c;用以评估一个词…

机器人大会引领产业动向,卓翼飞思绘制无人系统教科研新蓝图

8月21日&#xff0c;万众瞩目的2024世界机器人大会暨博览会在北京亦创国际会展中心盛大开幕。这场为期5天&#xff0c;集“展览”“论坛”“赛事”于一体的机器人盛会&#xff0c;反映了当下机器人领域的繁荣生态。据官方统计数据&#xff0c;今年现场逛展观众高达25万人次&…

揭秘!糖尿病:从绝望到希望的治愈之路

在这个快节奏、高压力的时代&#xff0c;糖尿病这一“甜蜜的负担”正悄然影响着越来越多人的生活。面对这一全球性的健康挑战&#xff0c;许多患者心中都萦绕着一个共同的疑问&#xff1a;“糖尿病&#xff0c;真的能治好吗&#xff1f;”今天&#xff0c;就让我们一起揭开糖尿…

《黑神话:悟空》:30%抽成真相

《黑神话&#xff1a;悟空》自建服务器出售&#xff1f;揭秘游戏界的30%抽成真相&#xff01; 近年来&#xff0c;随着游戏行业的迅猛发展&#xff0c;游戏开发商与发行平台之间的利益分配问题逐渐成为业界关注的焦点。其中&#xff0c;《黑神话&#xff1a;悟空》作为一款备受…

排序算法:

冒泡排序&#xff1a; 从列表的第一个数字开始进行比较&#xff0c;判断该数和下一个数之间的大小关系&#xff0c;如果该数比右边的数大&#xff0c;则交换位置&#xff1b;否则不变。一般一轮可以确定最大的数字&#xff0c;在列表的最后一位。 代码&#xff1a; 注意&…

【设计模式】创建型模式——简单工厂模式

文章目录 一、创建型模式1. 包含模式1.1 工厂模式1.2 建造者模式1.3 原型模式1.4 单例模式 二、工厂模式1. 概要1.1 意图1.2 主要解决问题1.3 何时使用1.4 如何解决1.5 关键代码1.6 使用场景1.7 优点1.8 缺点1.9 简单工厂实现 2. 简单工厂模式2.1 模式结构2.2 实例2.3 模式定义…

python实战一:合并多个Excel中数据

看不懂可以跟着视频学一下&#xff0c;关于基础课程项目也可以私聊我&#xff01; 视频源码&#xff1a; 链接&#xff1a;https://pan.quark.cn/s/2055653f735b 之前我们已经学习了如何读取和写入Execl数据&#xff0c;今天我们来用一个实例来进行表格的合并。如下是 2021年…

YOLOv9改进策略【模型轻量化】| ShufflenetV2,通过通道划分构建高效网络

一、本文介绍 本文记录的是基于ShufflenetV2的YOLOv9目标检测轻量化改进方法研究。FLOPs是评价模型复杂独的重要指标&#xff0c;但其无法考虑到模型的内存访问成本和并行度&#xff0c;因此本文在YOLOv9的基础上引入ShufflenetV2&#xff0c;使其在在保持准确性的同时提高模型…

多线程篇(基本认识 - 锁优化)(持续更新迭代)

目录 一、前言 二、阿里开发手册 三、synchronized 锁优化的背景 四、Synchronized的性能变化 1. Java5之前&#xff1a;用户态和内核态之间的切换 2. java6开始&#xff1a;优化Synchronized 五、锁升级 1. 无锁 2. 偏向锁 2.1. 前言 2.2. 什么是偏向锁 2.3. 偏向…

ChatTTS 长音频合成和本地部署2种方式,让你的“儿童绘本”发声的实战教程(文末有福利)

接上文&#xff08;GLM-4-Flash 大模型 API 免费了&#xff0c;手把手构建“儿童绘本”应用实战&#xff08;附源码&#xff09;&#xff09;&#xff0c;老牛同学通过 GLM-4-Flash 文生文和 CogView-3 文生图大模型&#xff0c;和大家一起编写了一个图文并茂的儿童绘本应用&am…

从默默无名到销量激增,极越CEO哭了

文 | 智能相对论 作者 | 周晚 8月28日&#xff0c;极越汽车CEO夏一平在接受媒体采访的时候泪洒当场&#xff01; 倒不是因为极越汽车销量寥寥&#xff0c;而是因为突然销量激增&#xff0c;他带领的极越团队走出低谷&#xff0c;当他们回过头去看今年上半年的巨大压力&#…

欧拉 函数

互质&#xff1a; 互质是公约数只有1的两个整数&#xff0c;叫做互质整数。公约数只有1的两个自然数&#xff0c;叫做互质自然数&#xff0c;后者是前者特殊情况。 &#xff08;1和-1与所有整数互质&#xff0c;而且它们是唯一与0互质的整数&#xff09; 互质的判断方法&…

1500+ HuggingFace API token暴露,使数百万 Meta-Llama、Bloom 等大模型用户面临风险

HugginingFace 和 GitHub 是AI开发人员的重要平台&#xff0c;因此我们决定对这些平台做更为深入的研究。在这项研究中&#xff0c;我们发现了数以千计的 API 令牌&#xff0c;这些令牌暴露给恶意行为者&#xff0c;使数百万最终用户可能受到攻击。 本文的主要内容包括&#x…

MySQL高阶练习题1- 寻找面试候选人

目录 题目 准备数据 分析数据 实现代码 总结 题目 返回 所有面试候选人 的姓名 name 和邮件 mail 。当用户满足以下两个要求中的 任意一条 &#xff0c;其成为 面试候选人 : 该用户在 连续三场及更多 比赛中赢得 任意 奖牌。该用户在 三场及更多不同的 比赛中赢得 金牌&…

python实战三-提取Word数据到Excel

视频源码&#xff1a; 链接&#xff1a;https://pan.quark.cn/s/83db5bb15383 一个文件夹下有大量会议通知文件&#xff0c;为word文件&#xff0c;文件格式都是一致的&#xff0c;现在要将文件中的一些字段提取出来汇总到Excel文件中。 会议通知文件格式如下&#xff1a; 要提…

C++(1)基础语法

C(1)之基础语法 Author: Once Day Date: 2024年8月29日 一位热衷于Linux学习和开发的菜鸟&#xff0c;试图谱写一场冒险之旅&#xff0c;也许终点只是一场白日梦… 漫漫长路&#xff0c;有人对你微笑过嘛… 全系列文章可参考专栏: 源码分析_Once-Day的博客-CSDN博客 参考文…

通用后台管理系统实战演示(Vue3 + element-plus)汇总篇一

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…